let originArray = [1, [2, 3], [4, 5], {a: 6}];
let cloneArray = ...;
cloneArray[0] = 10;
cloneArray[1] = [20, 30]
cloneArray[2].push('new');
cloneArray[3].a = 60;
1 赋值
对于基本类型赋值,系统会为新的变量在栈内存中分配一个新值
对于引用类型赋值,系统会为新的变量在内存中分配指向此对象的引用
let cloneArray = originArray;
console.log(originArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2 浅拷贝
如果拷贝的是对象类型,只会对第一层进行拷贝,不能对多维对象进行深拷贝
如果对象的属性是一个对象,此时拷贝的就是此属性的引用
2.1 originArray.concat()
返回值是对数组的第一层进行深拷贝
let cloneArray = originArray.concat();
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2.2 originArray.slice()
返回值是对数组的第一层进行深拷贝
let cloneArray = originArray.slice();
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2.3 Object.assign(originObject)
返回值是对对象的第一层进行深拷贝
2.4 解构赋值(...
·)
返回值是对目标的第一层进行深拷贝
let cloneArray = [...originArray];
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2.5 自定义
function shallowClone(obj) {
if (obj != null && typeof obj === 'object') {
let newobj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newobj[key] = obj[key]
}
}
return newobj
}
else {
return obj;
}
}
let cloneArray = shallowClone(originArray);
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
3 深拷贝
对目标的完全拷贝,值也被复制
深拷贝之后,两者不会互相影响
3.1 JSON.parse(JSON.stringify(obj))
会忽略undefined
会忽略symbol
会忽略函数,无法实现对象中方法的深拷贝
不能解决循环引用的对象 (会报错)
let cloneArray = JSON.parse(JSON.stringify(originArray));;
console.log(originArray) // [1, [2, 3], [4, 5], {a: 6}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
3.2 递归
function deepClone(obj) {
if (obj != null && typeof obj === 'object') {
let newobj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newobj[key] = deepClone(obj[key])
}
}
return newobj
}
else {
return obj;
}
}
let cloneArray = deepClone(originArray);
console.log(originArray) // [1, [2, 3], [4, 5], {a: 6}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
循环引用时不能用上述方法
3.3 lodash中的cloneDeep()
let cloneArray = cloneDeep(originArray);
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
3.4 递归解决循环引用 + symbol
function deepClone(obj, hash = new WeakMap()) {
if (obj != null && typeof obj === 'object') {
if (hash.has(obj)) {
return hash.get(obj);
}
let newobj = Array.isArray(obj) ? [] : {};
hash.set(obj, newobj);
let symKeys = Object.getOwnPropertySymbols(obj)
if (symKeys.length) {
symKeys.forEach(symKey => {
newobj[symKey] = cloneShallow(obj[symKey], hash)
})
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newobj[key] = deepClone(obj[key], hash)
}
}
return newobj
}
else {
return obj
}
}
let b = {}
let originArray = [1, [2, 3], [4, 5], {a: b}];
b['a'] = originArray
console.log(originArray) // [1, [2, 3], [4, 5], {a: [1, [2, 3], [4, 5], {...}]}] 无限套娃
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
例子
let obj = {
a: 1,
b: [1, 2],
c: {d: 1},
[Symbol('d')]: 1,
e: Symbol('1'),
f: undefined,
g: function(){}
}
3.5 迭代解决循环引用
将待拷贝的对象放入栈中,循环直至栈为空,解决了递归方法的爆栈问题
https://blog.csdn.net/qq_39989929/article/details/100065835
4 总结
赋值 | 浅拷贝 | 深拷贝 | |
---|---|---|---|
和原数据类型是否指向同一对象 | 是 | 否 | 否 |
第一层数据为基本数据类型 | 改变会使原数据一同修改 | 改变不会使原数据同一修改 | 改变不会使原数据同一修改 |
原数据第一层中包含对象 | 改变会使原数据一同修改 | 改变会使深于第一层的数据修改 | 改变不会使原数据同一修改 |
参考:https://github.com/axuebin/articles/issues/20
https://github.com/wind-jyf/blog/issues/1
循环引用
https://github.com/sqshada/Study-day/issues/2