先引用一篇文章JavaScript中的堆栈,作者写得很好。
如果看完上面推荐的文章还没有理解堆栈存与深拷贝浅拷贝的关系,请看?。
引用类型(对象“Object”,数组“Array”,函数“Function”)
引用类型不会在栈内存中保存值,只会在栈内存保存一个指向堆内存的地址。
当引用栈内容的地址的时候,会通过地址查询堆内存的值。
因此就有了浅拷贝深拷贝的区别。
浅拷贝 -> 只是拷贝了栈内存的地址。
深拷贝 -> 生成一个新的堆栈存,拷贝的同时生成一个新的堆内存。
很多人只知道 深拷贝浅拷贝,根本不知道其实现方式,或者说只知道 递归 、JSON.parse(JSON.stringify(obj)) 可以用来实现深拷贝,但并不理解是如何实现的。
我们通过一个数组来说明深拷贝的实现原理。
我们在这里只做一维数组的实现说明(浅显易懂)。对于多维数组的实现就需要通过其他方式 比如 ?递归遍历。
var arr = [1, 2, 3];
// var arr2 = arr; // arr2 只是栈内存的一个地址,当引用时会通过栈内存的地址去查找堆内存中的值
// 通过遍历的方式可以重新分配堆内存,其实就是拿arr的堆内存一个一个赋值给arr2的栈内存的地址。
var arr1 = [];
for (var i = 0; i < arr.length; i++) {
arr1.push(arr[i]);
}
arr1[1] = 5; // 修改arr1[1]
console.log(arr, arr1); // [1, 2, 3],[1, 5, 3]// 我们拿arr2来解析,其实也很简单 就是把堆内存重新赋值给另一个引用类型。因此实现深拷贝。
var arr2 = [];
arr2[0] = 1;
arr2[1] = 2;
arr2[2] = 3;
arr2[1] = 4; // 修改arr2[1]
console.log(arr, arr2); // [1, 2, 3],[1, 4, 3]
对于对象的深拷贝,以及深度优先遍历实现深拷贝方式。
慎用递归,浏览器每次执行代码时,都会分配一定尺寸的栈空间(Windows系统中为1M)。
附上JSON.parse(JSON.stringify(obj)) 的弊端
let func = new (function() {
this.name = '?';
})();
let objc = {
a: 1,
b: function() { // undefined 直接被忽略
this.name = '?';
},
c: NaN, // 转化成 null
d: new Date(), // 会被转化成字符串
e: func // 构造函数不拷贝 constructor
};
let A = JSON.parse(JSON.stringify(objc));
console.log(A, objc);
对比