js之深拷贝和浅拷贝
- 什么是深拷贝、浅拷贝。
- 赋值操作符与深拷贝及浅拷贝的区别。
- 如何优雅的实现深拷贝及浅拷贝。
1、什么是深拷贝、浅拷贝。
深拷贝和浅拷贝都是重新在内存中开辟一块区域,如果属性值为基本数据类型,深拷贝与浅拷贝并无区别,拷贝的都是基本数据类型的值,深拷贝与浅拷贝的主要区别在于对象的属性为引用数据类型时。
- 深拷贝:重新开辟一个新的区域(堆内存空间)存放数据,从内存中拷贝一个完整的新对象出来,此时,拷贝对象和原对象之间为两两独立的对象,互不影响。
- 浅拷贝:浅拷贝的拷贝的是原对象的引用,这就意味着如果原对象的该属性值改变了,那么新的拷贝对象的属性值也会发生改变。
const obj = {
foo: {
bar: {
name: "name1"
}
}
}
// 浅拷贝
const shallowObj = shallowClone(obj);
console.log(obj.foo === shallowObj.foo); // true
// 深拷贝
const deepObj = deepClone(obj);
console.log(obj.foo === deepObj.foo); // false
2、赋值操作符与深拷贝及浅拷贝的区别。
赋值操作符与深拷贝,浅拷贝的主要区别在于它不会开辟一个新的内存空间去存放拷贝对象,而是一个原对象的引用,它和原对象指向同一份内存地址,简单的理解,就相当于原对象的别名,这俩其实是一个东西。
// 赋值运算符
const _obj = obj;
_obj.foo.bar.name = "name2";
console.log(_obj === obj); // true
console.log(obj); // { foo: { bar: { name: "name2" } } }
console.log(_obj); // { foo: { bar: { name: "name2" } } }
// 浅拷贝
const shallowObj = Object.assign({}, obj);
shallowObj.foo.bar.name = "name3";
console.log(obj); // { foo: { bar: { name: "name3" } } }
console.log(shallowObj); // { foo: { bar: { name: "name3" } } }
// 深拷贝
const deepObj = JSON.parse(JSON.stringify(obj));
deepObj.foo.bar.name = "name4";
console.log(obj); // { foo: { bar: { name: "name3" } } }
console.log(deepObj); // { foo: { bar: { name: "name4" } } }
3、如何优雅的实现深拷贝及浅拷贝。
浅拷贝
1、Object.assign()
使用Objext.assign()方法可以将其他对象复制到原对象中,并将复制后的原对象返回。
const obj = {
foo: {
bar: {
name: "name1"
}
}
};
const _obj = Object.assign({}, obj);
2、... 展开运算符
使用 ES6 的 ... 展开运算符也可以实现浅拷贝。
const obj = {
foo: {
bar: {
name: "name1"
}
}
};
const _obj = ...obj;
3、Array.prototype.concat()
const arr = [1, 2, { foo: "foo" }];
const _arr = arr.concat();
4、Array.prototype.slice()
const arr = [1, 2, { foo: "foo" }];
const _arr = arr.concat();
5、手动实现浅拷贝
function shallowClone(obj) {
if (obj === null || obj === undefined) return obj;
const _obj = obj;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
_obj[key] = obj[key];
}
}
return _obj;
};
深拷贝
1、使用 JSON.parse(),JSON.stringify()
这个方法有个问题,就是无法处理函数和正则,函数会丢失,正则会转换为 null 对象。
const obj = {
foo: {
bar: {
name: "name1"
}
}
};
const _obj = JSON.parse(JSON.stringify(obj));
2、手动实现深拷贝
这里主要是使用 WeakMap来解决对象的循环引用问题。
function deepClone(obj, weakMap = new WeakMap()) {
// 如果是 null、undefines或者不是 object 类型,那么直接返回
if (obj === null || obj === undefined || typeof obj === "object") {
return obj;
}
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
// 使用 WeakMap 处理循环引用问题
if (weakMap.has(obj)) {
return weakMap.get(obj);
}
const cloneObj = new obj.constructor();
weakMap.set(obj, cloneObj);
for (const key in cloneObj) {
if (obj.hasOwnProperty()) {
cloneObj[key] = deepClone(obj[key], weakMap);
}
}
return cloneObj;
};
const test = {
aaa: 111
}
test.bbb = test;
console.log(deepClone(test));