前端深拷贝实现
前端深拷贝实现
1. JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj))实现深拷贝的过程是利用JSON.stringify()将对象序列化(转换成一个JSON字符串),再使用JSON.parse()进行反序列化(还原)。
弊端
如果 obj 里有时间对象,则JSON.parse(JSON.stringify(obj))的结果将只是字符串,而不是时间对象:
const date = new Date();
const newDate = JSON.parse(JSON.stringify(date));
console.log(typeof newDate); // string
如果 obj 里有 RegExp、Error 对象,则序列化的结果将只会得到空对象:
const reg = new RegExp();
const err = new Error();
const newReg = JSON.parse(JSON.stringify(reg));
const newErr = JSON.parse(JSON.stringify(err));
console.log(newReg); // {}
console.log(newErr); // {}
如果 obj 里有函数、undefined,则序列化的结果会将函数、undefined 丢失:
const object = {
fn: () => {},
x: undefined,
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject); // {}
如果 obj 里有 NaN、Infinity、-Infinity,则序列化的结果会得到null:
const object = {
x: NaN,
y: Infinity,
z: -Infinity,
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject); // {x: null, y: null, z: null}
JSON.stringify()只能序列化对象的可枚举的自有属性,如果 obj 中有对象是构造函数生成的,则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢失对象的 constructor:
function Person(name, age) {
this.name = name;
this.age = age;
}
const dlrb = new Person('迪丽热巴', 18);
const object = {
x: dlrb
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject.x.__proto__.constructor === Person); // fasle
如果 obj 中存在循环引用,会陷入死循环:
const object = {
x: 1,
}
object.object = object;
const newObject = JSON.parse(JSON.stringify(object)); // error
排除以上情况,可以使用JSON.parse(JSON.stringify(obj))来实现深拷贝。
2. 封装deepClone函数
function deepClone(target){
if(target !== null && typeof target === 'object'){
let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {};
for (let k in target){
if (target.hasOwnProperty(k)) {
result[k] = deepClone(target[k])
}
}
return result;
}else{
return target;
}
}
以上代码中,deepClone函数的参数 target 是要深拷贝的数据。
执行 target !== null && typeof target === 'object' 判断 target 是不是引用类型。
若不是,直接返回 target。
若是,判断引用类型的数据是对象类型还是数组类型,然后创建一个变量 result 作为深拷贝的结果,遍历 target,执行 deepClone(target[k]) 把 target 每个属性的值深拷贝后赋值到深拷贝的结果对应的属性 result[k] 上,遍历完毕后返回 result。
在执行 deepClone(target[k]) 中,又会对 target[k] 进行类型判断,重复上述流程,形成了一个递归调用 deepClone 函数的过程。就可以层层遍历要拷贝的数据,不管要拷贝的数据有多少子属性,只要子属性的值的类型是引用类型,就会调用 deepClone 函数将其深拷贝后赋值到深拷贝的结果对应的属性上。
另外使用 for...in 循环遍历对象的属性时,其原型链上的所有属性都将被访问,如果只要只遍历对象自身的属性,而不遍历继承于原型链上的属性,要使用 hasOwnProperty 方法过滤一下。
0
0
0
0
0
0






