Deep Clone | 深拷贝
所有元素或属性均完全克隆,并于原引用类型完全独立,即,在后面修改对象的属性的时候,原对象不会被修改。
在 JavaScript 中复制一个对象几乎都是浅拷贝而不是深拷贝。
浅拷贝
浅拷贝只是复制了对象的引用,而不是对象本身。
const me = {
name: 'ddd',
age: 21,
girlFriend: {
name: 'bbb',
age: 81
}
};
const meCopy = {...me};
me.age = 22;
console.log(me.age);// 21
me.girlFriend.age = 82;
console.log(me.girlFriend.age);// 82
有七种原始数据类型:string、number、bigint、boolean、undefined、symbol 和 null。
深拷贝
考虑几个因素:
- 传入的对象是使用对象字面量
{}创建的对象还是由构造函数生成的对象 - 如果对象是由构造函数创建出来的,那么是否要拷贝原型链上的属性
- 如果要拷贝原型链上的属性,那么如果原型链上存在多个同名的属性,保留哪个
- 如果对象中存在循环引用的情况,如何处理
JSON
使用简单,且兼容性好
它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。
let _clone = JSON.parse(JSON.stringify(obj));
弊端:
obj对象存在循环引用,那么该方法会报错- 对象存在
Date对象,该方法会将其转化成字符串 obj对象中存在Set、Map、正则、Error对象,该方法会将其转成空对象字面量{ },如果存在undefined,该方法会直接忽略
Lodash.cloneDeep
兼容性好,功能强大,对于 Function 类型不会报错
注意处理 tree-shaking
/**
*
* @param {*} value 要深拷贝的值
* @returns 返回深拷贝后的值
*
*/
_.cloneDeep(value)
structuredClone
浏览器原生支持, 兼容性问题
/**
* @param {*} value 被克隆的对象。可以是任何结构化克隆支持的类型。
* @param {*} options 是一个可转移对象的数组,里面的 值 并没有被克隆,而是被转移到被拷贝对象上。
* @returns 返回一个与被克隆对象结构相同的对象。
*
*/
structuredClone(value)
structuredClone(value, { transfer })
基本实现
function deepClone(obj) {
if (obj === null) return null;
if (typeof obj !== 'object') return obj;
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) { //不遍历其原型链上的属性
let val = obj[key];
newObj[key] = typeof val === 'object' ? deepClone(val) : val;
}
}
return newObj;
}