持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
在项目开发中,深浅拷贝的使用是非常常见的场景,拷贝之所以有深浅,是因为JS 数据类型之间的差异,导致存放的位置不同(栈和堆)
JS 的数据类型分为:
- 基本数据类型
- 基本数据类型存放在栈中
- 存储基本数据类型及引用类型数据的堆地址
- 引用数据类型
- 引用数据类型存放在堆中
- 存储引用类型数据
1. 浅拷贝
浅拷贝是拷贝一层,深层次的引用类型则共享内存地址。新对象与旧对象共享同一块内存,修改对象属性会影响原对象
可实现浅拷贝的方法:
- Object.assign
- const newObj = Object.assign({}, obj);
- Array.prototype.slice(), Array.prototype.concat()
- const arr1 = arr.slice(0)
- const arr2 = arr.concat()
- 使用...(拓展运算符)实现
代码实现
- obj.hasOwnProperty() 是 Object 的原型方法,检测属性是否为对象的自有属性
- obj.hasOwnProperty('c1') // false, obj对象没有c1属性 (oldData中的a、b、c都可以浅拷贝,c1不能)
function shallowClone(obj) {
const newObj = {};
for(let item in obj) {
if(obj.hasOwnProperty(item)){
newObj[item] = obj[item];
}
}
return newObj;
}
const oldData = {
a:1,
b:2,
c: {
c1:3
}
}
const newData = shallowClone(oldData)
newData.a = 10
newData.c.c1 = 200
console.log(`oldData.a: ${oldData.a}`)
console.log(`newData.a: ${newData.a}`)
console.log(`oldData.c.c1: ${oldData.c.c1}`)
console.log(`newData.c.c1: ${newData.c.c1}`)
运行结果:
2. 深拷贝
深拷贝时,新生成一个站栈,新对象与原对象不共享内存,修改新对象的值不会影响原对象
可实现深拷贝的方法:
- _.cloneDeep()
- const _ = require('lodash'); const obj1 = _.cloneDeep(obj);
- jQuery.extend()
- JSON.stringify()
- 弊端:会忽略 undefined、symbol 和函数,当数据为函数的时候,拷贝的结果为null;当数据为正则的时候,拷贝结果为一个空对象{}
- 手写一个深拷贝函数
代码实现
手写一个深拷贝函数,可以解决其它方式拷贝不了函数以及 undefined 的问题
注: 普通的递归无法解决循环引用问题,所以使用 WeakMap:
WeakMap
- WeakMap 仅接受对象(Map 不限制)
- WeakMap是一种弱引用,所以不会造成内存泄漏(下一次垃圾回收机制执行时,内存会被释放掉)
- 方法:delete、get、has、set
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; // 当传入的参数是null或者undefined时,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(); // 深拷贝
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);// 递归
}
}
return cloneObj;
}
const oldData = {
a:1,b:2,
c: {
c1:3
}
}
let newData = deepClone(oldData)
newData.a = 10
newData.c.c1 = 200
console.log(`oldData.a: ${oldData.a}`)
console.log(`newData.a: ${newData.a}`)
console.log(`oldData.c.c1: ${oldData.c.c1}`)
console.log(`newData.c.c1: ${newData.c.c1}`)
运行结果: