携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
今天聊聊前端中对象的几种常见的拷贝方式,以及限制条件。
1.Object.assign(只会拷贝源对象可枚举的和自身的属性到目标对象)
这种方式是比较常见的方式,大家应该也都用过。我这截了一张官方文档对Object.assign方法的说明。
在使用的时候,需要注意(Object.assign() 不会在 source 对象值为 [null]时抛出错误)
并且,关于深浅拷贝,文档也有具体的说明
所以说,这是一个典型的浅拷贝方法。
2. es6的对象解析
对象的解析,其实就是一种key,value的重新赋值,很明显也是一种浅拷贝
let a = {
age: 12,
like: ['apple', 'banana']
}
let b = { ... a }
a.like[0] = 'orange'
console.log(b) // { age: 12, like: ['orange', 'banana'] }
3.JSON.stringify/structuredClone
JSON也是我们经常用的一种方法,但是它也是有限制条件的。
在文档上有这样一句话,“One way to make a deep copy of a JavaScript object, if it can be [serialized]”
如果数据是可序列化的,那么可以用JSON来进行深拷贝。
然而,一旦数据不能序列化,那么使用JSON进行拷贝就会有一定的问题,比如像函数,dom元素,symbols之类的数据。
关于这一点,文档中也有具体的说明。
在文档的最后,还还介绍了一种深拷贝方式,structuredClone方法,相比较于JSON,两者之间的用法很相似,唯一的区别是structuredClone可以转移源。
**4.MessageClone **
这个方法其实并不算是用来拷贝的,而是用于iframe通信的,但是这个方法也是可以实现数据的深拷贝的,只不过比较冷门,这里也做一下介绍。
const MessageClone = (obj) => {
return new Promise((resolve) => {
const { port1, port2 } = new MessageChannel();
port2.onmessage = (mes) => resolve(mes.data);
port1.postMessage(obj);
});
};
let obj = {
a: {
b: 1,
arr: [1, 2, 3, 4],
},
}, newObj = null;
MessageClone(obj).then((obj) => {
newObj = obj;
console.log(newObj);
});
5.递归
介绍了这么多,目前还没有一个特别完善的方法。
虽然之前的方法都可以实现拷贝,但多少都有一点问题,比如我们要拷贝原型链方法,函数之类的数据。
所以想要彻底的进行深拷贝,还是得用递归方法。
目前,像lodash之类的框架,里面的深拷贝方法也是用的递归方法。
这里实现一个简单的版本,只考虑相对简单的情况, 像lodash里面是加了很多判断,以及类型兼容的,有兴趣可以看一下lodash中的深拷方法。
注意: (为了能拷贝原型链的方式,使用对象的构造函数来创建变量。)
function deepClone(data) {
let obj = null;
if (Object.prototype.toString.call(data) === "[object Array]") {
obj = new data.constructor();
for (let i = 0; i < data.length; i++) {
obj.push(deepClone(data[i]));
}
} else if (Object.prototype.toString.call(data) === "[object Object]") {
obj = new data.constructor();
for (let key in data) {
if (data.hasOwnProperty(key)) {
obj[key] = deepClone(data[key]);
}
}
} else {
return data;
}
return obj;
}
目前来说,递归方法是唯一一个没有副作用的方法,唯一的缺点就是实现比较麻烦。