一个老生常谈的问题,也是 挤压很久的文章。也是 我业务代码赋值传递过程进场遇到的问题。其实之前没有深浅拷贝的概念。只是是与空的对象/数组合并生成新的对象/数组,或者是正反序列化,避免指向原数组/对象。后来查看InterviewMap 才知道深浅拷贝概念。所以简单 记录保存一下吧…
前言
一个赋值问题:
let obj1 = {
age: 1
};
let obj2 = obj1;
obj2.age = 2;
console.log(obj1.age); // 2 obj1的值也改变了
是的,对象/数组的简单复制,并不是对象/数组本身, 而是指向改对象的指针。 所以两个 变量(obj1、obj2)指向的是同一个对象。
怎么解决呢?深浅拷贝,其实原理感觉都是为了生成新的对象/数组
浅拷贝
循环遍历赋值
我们知道,复制一个基本类型的值时,会创建一个新值,并把它保存在新的变量的位置上。简单来说就是一个一个复制对象/数组中的单一值。 基本上下面的也是相同的原理。
扩展运算符
将一个对象/数组转为用逗号分隔的参数序列。
let obj1 = {
age: 1
};
let obj2 = { ...obj1 };
obj2.age = 2;
console.log(obj1.age); // 1
Object.assign(target, …sources)
当然 js 标准 库中也提供了一些方法。Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
let obj1 = {
age: 1
};
let obj2 = Object.assign({}, obj1);
obj2.age = 2;
console.log(obj1.age); // 1
Array.slice()
slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。且原始数组不会被修改。
深拷贝
正反序列化
JSON.parse(JSON.stringify(obj));
局限性:
- 忽略 undefined
- 不能序列化函数
- 不能深拷贝内对象
let obj1 = {
a: {
age: 1
},
b: 2
};
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1.a === obj2.a) // false
console.log(obj1.b === obj2.b) // false
console.log(obj1.a.age === obj2.a.age) // true
循环遍历赋值 + 递归
与上述方法相同,多了对内对象的再次循环赋值处理。当然你可以封装一个方法递归处理。
如果你想深复制一个对象(那就是沿着原形链,对所有属性进行递归复制),你必须要用另外一种方法。以下是一个可行的例子。结构化克隆算法-深复制
function clone(objectToBeCloned) {
// Basis.
if (!(objectToBeCloned instanceof Object)) {
return objectToBeCloned;
}
var objectClone;
// Filter out special objects.
var Constructor = objectToBeCloned.constructor;
switch (Constructor) {
// Implement other special objects here.
case RegExp:
objectClone = new Constructor(objectToBeCloned);
break;
case Date:
objectClone = new Constructor(objectToBeCloned.getTime());
break;
default:
objectClone = new Constructor();
}
// Clone each property.
for (var prop in objectToBeCloned) {
objectClone[prop] = clone(objectToBeCloned[prop]);
}
return objectClone;
}
其他工具库
- loadash: loadash 的深拷贝函数
- 尤大封装的循环使用JSON.stringify和JSON.parse:circular-json-es6