在说 JSON.stringify() 深拷贝之前,我们先说一说深拷贝和浅拷贝的事情吧。
目录
1 为什么要做深拷贝?
2 哪些做法算浅拷贝?
2.1 直接变量赋值
2.2 Object.assign 浅拷贝
3 哪些做法算深拷贝 ?
3.1 JSON.stringify()
3.2 cloneDeep()
3.3 面试的时候要求手动实现
1 为什么要做深拷贝?
我们知道javascript基本数据类型,是存在栈内存中,复制相对简单,而引用数据类型,则存在堆内存中,引用数据类型实际是把变量指向了一个内存地址。
栈内存不存在内存地址,所以拷贝前后,新的变量可以不影响旧的变量,但堆内存,单纯的拷贝,还是会公用内存地址,所以修改新的引用数据类型变量,旧的还是会被影响到。所以,拷贝引用数据类型变量的时候,就需要新开一个内存地址,让新的变量指向新的内存地址,从而不影响旧的变量。
2 哪些做法算浅拷贝?
2.1 直接变量赋值
比如我们新创建一个变量,进行等号赋值
<script>const obj1 = {name: 'jim',name2: undefined} const obj2 = obj1;obj2.name2 = 'tom';console.log('obj1的改变:', obj1);
</script>
可以发现,我们改变了obj2的name2属性,但obj1也跟着改变了。
2.2 Object.assign 浅拷贝
Object.assign 是一个浅拷贝,我们看一下代码
<script>var obj1 = {name: 'qiaozhi',sister: ['peiqi', 'suxi'],parent: {mother: 'zhumama',father: 'zhubaba'},do: function () {console.log('I want jump nikeng!')}}var obj2 = Object.assign({}, obj1);obj2.name = 'qiaozhi-new';obj2['sister'][0] = 'peiqi-new';obj2['parent']['mother'] = 'zhumama-new';console.log('查看一下obj1::', obj1);</script>
可以看出, 经过Object.assign拷贝后,基本数据变量得到了拷贝,但数组,对象还是会被修改
3 哪些做法算深拷贝 ?
3.1 JSON.stringify()
JSON.stringify() 是比较常用的深拷贝方案,但他对于一些值会被忽略掉,比如undefined,symbol和函数
<script>var obj1 = {name: 'qiaozhi',name2: undefined,sister: ['peiqi', 'suxi'],parent: {mother: 'zhumama',father: 'zhubaba'},do: function () {console.log('I want jump nikeng!')}}var obj2 = JSON.parse(JSON.stringify(obj1));console.log('看看我的深拷贝ojb2:', obj2);
</script>
你看,name2和do这个属性找不到了。虽然如此,这其实对于平时的业务倒是影响不太大。
3.2 cloneDeep()
这个实现需要引入lodash工具
<script>const lodash = require('lodash');var obj1 = {name: 'qiaozhi',name2: undefined,sister: ['peiqi', 'suxi'],parent: {mother: 'zhumama',father: 'zhubaba'},do: function () {console.log('I want jump nikeng!')}}var obj2 = lodash.cloneDeep(obj1)console.log('看看我的深拷贝ojb2:', obj2);</script>
3.3 面试的时候要求手动实现
面试的时候,很多时候要求你写一个方法,可以实现深度拷贝,代码如下:
function deepClone(obj, hash = new WeakMap()) {if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作if (obj instanceof Date) return new Date(obj);if (obj instanceof RegExp) return new RegExp(obj);// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝if (typeof obj !== "object") return obj;// 是对象的话就要进行深拷贝if (hash.get(obj)) return hash.get(obj);let cloneObj = new obj.constructor();// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身hash.set(obj, cloneObj);for (let key in obj) {if (obj.hasOwnProperty(key)) {// 实现一个递归拷贝cloneObj[key] = deepClone(obj[key], hash);}}return cloneObj;
}