---本文此前有诸多错误,修改于2023/10/31,如有别的文章还有错误请联系yctjb1@126.com
图示中mdn有明确说明“现在数组a也被也影响了”。即深层遍历的时候(非只遍历一层),它拷贝的是引用,此时便是浅拷贝。
let a = [[1],[2],[3]];
let b = [...a];
b.shift().shift(); // 浅拷贝的话,此操作会影响a中的内容
console.log(a) // Array [Array [], Array [2], Array [3]] --受影响
console.log(b) // Array [Array [2], Array [3]]
//========换成普通对象来对上述情况更直观的验证
let a = {a1:{ a2:"a2" }};
let b = {...a};
a.a1.a2 = 'a2_改';
console.log(a) // Object { a1: Object { a2: "a2_改" } }
console.log(b) // Object { a1: Object { a2: "a2_改" } } --受影响
//========
let a = [['0'],['1']];
let b = [...a];
let c = Object.assign(a);
let d = Object.assign([],a);
a[0][0] = '0-改';
console.log(b) // Array [Array ["0-改"], Array ["1"]] --受影响
console.log(c) // Array [Array ["0-改"], Array ["1"]] --受影响
console.log(d) // Array [Array ["0-改"], Array ["1"]] --受影响
网络上有一个很常见的说法“如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没变,那就是深拷贝”。
但从展开运算符的例子就可以看出来,js中相当多的方法在只遍历一层的时候是深拷贝,深层遍历的时候是浅拷贝。因此很容易陷入一个'眼见为虚'的陷阱——即使观测到一个方法是深拷贝,它也是在特定条件的下的,比如‘仅仅在只遍历一层、且是基本类型数据’的时候是深拷贝方法。
concat方法与slice方法也是同样,即 扩展运算符、concat、slice 均仅对第一层的'基本数据类型'实现深拷贝
let a = [{a1_1:"A"},'B']
let b = [].concat(a);
let c = a.slice(0);
a[0].a1_1 = 'A改'; // {}非基本类型,发生的是浅拷贝
a[1] = 'B改';
console.log(b) // Array [Object { a1_1: "A改" }, "B"]
console.log(c) // Array [Object { a1_1: "A改" }, "B"]
下面这篇文章很好的从"栈堆,基本数据类型与引用数据类型"的角度出发去区分深拷贝与浅拷贝,此处暂不做过多阐述
https://www.cnblogs.com/echolun/p/7889848.html
笔者常用的一个深拷贝策略,就是文章中也提到的方法2:
对原对象先进行一次JOSN.stringify处理,再进行一次JSON.parse
let obj = {...}//原对象;
let objClone = JSON.parse(JSON.stringify(obj));
此外 js 也有原生的一个深拷贝方法 structuredClone
structuredClone() - Web API 接口参考 | MDN
let a1 = [['0'],['1']]
let a2 = {'0': { '0-0': '0-0'}}
let b = structuredClone(a1);
let c = structuredClone(a2);
let d = JSON.parse(JSON.stringify(a2))
a1[0][0] = '0改'
a2['0']['0-0'] = '0-0改'
console.log(a1) // Array [Array ["0改"], Array ["1"]]
console.log(a2) // Object { 0: Object { 0-0: "0-0改" } }
console.log(b) // Array [Array ["0"], Array ["1"]]
console.log(c) // Object { 0: Object { 0-0: "0-0" } }
console.log(d) // Object { 0: Object { 0-0: "0-0" } }