浅拷贝技巧:数组concat slice
,对象Object.assign()
等;
深拷贝技巧:JSON.parse(JSON.stringify(arr1))
。
数组的浅拷贝
如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或者数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化。
我们把这种复制引用的拷贝方法称之为浅拷贝,与之对应的就是深拷贝,深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。
比如,数组的一些方法:concat、slice
:
1 2 3 4 5 6 7 8 9
| var arr = ["old", 1, true, null, undefined];
var newArr = arr.concat(); newArr.shift(); console.log(arr); console.log(newArr);
var newArr2 = arr.slice(); console.log(newArr2);
|
但是如果数组嵌套了对象或者数组的话,就会都受影响,比如:
1 2 3 4 5 6
| var arrObj = [{ a: 1 }, { b: 2 }];
var newArrObj = arrObj.concat(); newArrObj[0].a = "aaa"; console.log(newArrObj); console.log(arrObj);
|
数组的深拷贝
使用 JSON.stringify()
和JSON.parse()
,不管是数组还是对象,都可以实现深拷贝,但是不能拷贝函数,会返回一个 null:
1 2 3 4 5
| var arr1 = ["old", 1, true, ["old1", "old2"], { old: 1 }, function() {}]; var newArr1 = JSON.parse(JSON.stringify(arr1)); newArr1.shift(); console.log(arr1); console.log(newArr1);
|
Object.assign
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
1 2
| Object.assign(target, ...sources);
|
Object.assign
方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]
和目标对象的[[Set]]
,所以它会调用相关 getter 和 setter。
String 类型和 Symbol 类型的属性都会被拷贝。
1 2 3 4
| const obj = { a: 1 }; const copy = Object.assign({}, obj); console.log(copy);
|
针对深拷贝,需要使用其他办法,如借助 JSON,因为 Object.assign()
拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
1 2 3 4 5
| obj1 = { a: 0, b: { c: 0 } }; let obj3 = JSON.parse(JSON.stringify(obj1)); obj1.a = 4; obj1.b.c = 4; console.log(JSON.stringify(obj3));
|
浅拷贝的实现
技巧型的拷贝,如上边使用的 concat、slice、JSON.stringify
等,如果要实现一个对象或者数组的浅拷贝,该怎么实现呢?
思路:既然是浅拷贝,那就只需要遍历,把对应的属性及属性值添加到新的对象,并返回。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var shallowCopy = function(obj) { if (typeof obj !== "object") return;
var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } }
return newObj; };
var arr20 = ["old", 1, true, ["old1", "old2"], { old: 1 }, function() {}]; var newArr20 = shallowCopy(arr20);
console.log({ newArr20 });
|
深拷贝的实现
思路:如果是对象,通过递归调用拷贝函数
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| var deepCopy = function(obj) { if (typeof obj !== "object") return; var newObj = obj instanceof Array ? [] : {};
for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] !== "object" ? obj[key] : deepCopy(obj[key]); } }
return newObj; };
var obj = { a: function() {}, b: { name: "Tony", age: 10 }, c: [1, 2, 3] };
var newObj = deepCopy(obj); console.log(newObj);
|
模拟 jQuery 的 extend
extend 的用法:合并两个或者更多的对象的内容到第一个对象中。
1
| jQuery.extend( [deep], target, object1 [, objectN ] )
|
- deep,布尔值,如果为 true,进行深拷贝;false 做浅拷贝,target 就往后移动到第二个参数
- target,表示要拓展的目标,我们就称它为目标对象吧。
- 后面的参数,都传入对象,内容都会复制到目标对象中,我们就称它们为待复制对象吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty;
function isPlainObject(obj) { var proto, Ctor; if (!obj || toString.call(obj) !== "[object Object]") { return false; } proto = Object.getPrototypeOf(obj); if (!proto) { return true; } Ctor = hasOwn.call(proto, "constructor") && proto.constructor; return ( typeof Ctor === "function" && hasOwn.toString.call(Ctor) === hasOwn.toString.call(Object) ); }
function extend() { var deep = false; var name, options, src, copy, clone, copyIsArray; var length = arguments.length; var i = 1; var target = arguments[0] || {}; if (typeof target == "boolean") { deep = target; target = arguments[i] || {}; i++; } if (typeof target !== "object" && !isFunction(target)) { target = {}; }
for (; i < length; i++) { options = arguments[i]; if (options != null) { for (name in options) { src = target[name]; copy = options[name];
if (target === copy) { continue; }
if ( deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy))) ) { if (copyIsArray) { copyIsArray = false; clone = src && Array.isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; }
target[name] = extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } }
return target; }
|
学习资料