最近看到面试题,是关于深拷贝的
然后就即兴思考了很多,写了本偏文章。
初版深拷贝,也就是视频里将的那版
function deepClone(source) {
// 输入是数组或是对象
let target = source.constructor === Array ? [] : {};
for (let key in source) {
// 遍历,如果是对象或是数组,递归拷贝
if (
source[key].constructor === Array ||
source[key].constructor === Object
) {
target[key] = deepClone(source[key]);
} else {
//否则是简单数据类型,直接赋值
target[key] = source[key];
}
}
return target;
}
这里有一个疑问就是数组的constructor可以被改变,也就是:
let a = [1, 2];
console.log(a.constructor);
a.constructor = Object;
console.log(a.constructor);
虽然if判断处处理逻辑是一样的,但是target处的判断有问题,
那么尝试修改判断方式,使用instanceof
console.log(a instanceof Array);
console.log(a instanceof Object);
但是显然,不管怎么判断都是true,方式不可行,还有办法吗
有的,使用Array.isArray
console.log(Array.isArray(a));
let b = { id: 0 };
console.log(Array.isArray(b));
可以看出,就算是constructor更改了,依然可以判断出Array
而且对象返回的是false,因此尝试重写深拷贝:
function deepClone(source) {
// 这里的输入只会数组或者是对象
let target = Array.isArray(source) ? [] : {};
for (let key in source) {
// 有线考虑数组
if (Array.isArray(source[key])) {
target[key] = deepClone(source[key]);
} else if (source[key].constructor === Object) {
// 接着考虑对象
target[key] = deepClone(source[key]);
// 再写其他数据类型的逻辑
} else {
target[key] = source[key];
}
}
return target;
}
可是代码太冗长了,而且逻辑是一样的,换一个思路
let obj = {
a: [1, 2],
b: { c: 1, d: "2", e: [1, 2], f: { g: 1, h: "abc" } },
i: "abc",
j: 1,
};
for (let key in obj) {
console.log(obj[key] instanceof Object);
}
可以区分数组和对象都返回true,而且处理逻辑相同,
那么方法还可以优化如下
function dc(source) {
let target = Array.isArray(source) ? [] : {};
for (key in source) {
if (source[key] instanceof Object) {
target[key] = deepClone(source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
let newobj = dc(obj);
console.log(obj);
newobj.x = "y";
console.log(newobj);
可以看到修改拷贝后的对象源对象没有受到改变
已知生活中可以使用以下方法实现深拷贝解决80%的问题:
let dcjson = (source) => {
return JSON.parse(JSON.stringify(source));
};
let newobj1 = dcjson(obj);
newobj1.x = "y";
console.log(obj);
console.log(newobj1);
剩下20%因为函数等无法拷贝而不能使用JSON
obj.y = function () {
console.log("aaa");
};
console.log(obj);
console.log(dc(obj));
console.log(dcjson(obj));
结果中,自编dc方法拷贝的函数已经不是函数,json方法未拷贝
那有办法拷贝函数吗,当然有,先看一个浅拷贝的方式
let newobj2 = { ...newobj };
console.log(newobj2);
// 函数是可以通过浅拷贝的方式被拷贝的
let newobj3 = { ...obj };
console.log(newobj3);
但是只适合部分场景
周所众知,有一个库叫lodash。所以想要拷贝包括函数在内的更多数据类型,还是用lodash库吧。
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
汇总:
//初版深拷贝
function deepClone(source) {
// 输入是数组或是对象
let target = source.constructor === Array ? [] : {};
for (let key in source) {
// 遍历,如果是对象或是数组,递归拷贝
if (
source[key].constructor === Array ||
source[key].constructor === Object
) {
target[key] = deepClone(source[key]);
} else {
//否则是简单数据类型,直接赋值
target[key] = source[key];
}
}
return target;
}
//这里有一个疑问就是数组的constructor可以被改变,也就是:
let a = [1, 2];
console.log(a.constructor);
a.constructor = Object;
console.log(a.constructor);
// 虽然if判断处处理逻辑是一样的,但是target处的判断有问题,
// 那么尝试修改判断方式,使用instanceof
console.log(a instanceof Array);
console.log(a instanceof Object);
// 但是显然,不管怎么判断都是true,方式不可行,还有办法吗
// 有的,使用Array.isArray
console.log(Array.isArray(a));
let b = { id: 0 };
console.log(Array.isArray(b));
// 可以看出,就算是constructor更改了,依然可以判断出Array
// 而且对象返回的是false,因此尝试重写深拷贝:
function deepClone(source) {
// 这里的输入只会数组或者是对象
let target = Array.isArray(source) ? [] : {};
for (let key in source) {
// 有线考虑数组
if (Array.isArray(source[key])) {
target[key] = deepClone(source[key]);
} else if (source[key].constructor === Object) {
// 接着考虑对象
target[key] = deepClone(source[key]);
// 再写其他数据类型的逻辑
} else {
target[key] = source[key];
}
}
return target;
}
// 可是代码太冗长了,而且逻辑是一样的,换一个思路
let obj = {
a: [1, 2],
b: { c: 1, d: "2", e: [1, 2], f: { g: 1, h: "abc" } },
i: "abc",
j: 1,
};
for (let key in obj) {
console.log(obj[key] instanceof Object);
}
// 可以区分数组和对象都返回true,而且处理逻辑相同,
// 那么方法还可以优化如下
function dc(source) {
let target = Array.isArray(source) ? [] : {};
for (key in source) {
if (source[key] instanceof Object) {
target[key] = deepClone(source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
let newobj = dc(obj);
console.log(obj);
newobj.x = "y";
console.log(newobj);
// 已知生活中可以使用以下方法实现深拷贝解决80%的问题
let dcjson = (source) => {
return JSON.parse(JSON.stringify(source));
};
let newobj1 = dcjson(obj);
newobj1.x = "y";
console.log(obj);
console.log(newobj1);
// 剩下20%因为函数等无法拷贝而不能使用JSON
obj.y = function () {
console.log("aaa");
};
console.log(obj);
console.log(dc(obj));
console.log(dcjson(obj));
// 结果中,自编dc方法拷贝的函数已经不是函数,json方法未拷贝
// 那有办法拷贝函数吗,当然有,先看一个浅拷贝的方式
let newobj2 = { ...newobj };
console.log(newobj2);
// 函数是可以通过浅拷贝的方式被拷贝的
let newobj3 = { ...obj };
console.log(newobj3);
// 但是只适合部分场景
// 周所众知,有一个库叫lodash,以下是lodash深拷贝源码
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false