浅拷贝 shallow clone
Object.assign
let target = {}
let source = {
a : {
b : 2
}
}
Object.assign(target, source)
console.log(target)
target.a.b = 10
console.log(source)
它不会拷贝对象的继承属性、对象的不可枚举的属性
它可以拷贝Symbol类型的属性
到了对象的第二层属性target.a.b这里的时候, 前者值的改变也会影响后者的第二层属性的值,说明其中依旧存在着访问共同堆内存的问题,也就是说这种方法还不能进一步复制,而只是完成了浅拷贝的功能
扩展运算符spread operator
let obj = {a:1,b:{c:1}}
let obj1 = {...obj}
obj1.a = 2
console.log(obj)
obj1.b.c = 2
console.log(obj)
/* 数组的拷贝 */
let arr = [1, 2, 3];
let newArr = [...arr];
扩展运算符 和 object.assign 有同样的缺陷,也就是实现的浅拷贝的功能差不多,但是如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。
concat
let arr = [1, 2, 3]
let newArr = arr.concat()
newArr[1] = 1000
console.log(arr, newArr)
slice
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr); //[ 1, 2, { val: 1000 }
从上面的代码中可以看出,这就是浅拷贝的限制所在了—它只能拷贝一层对象,如果存在对象的嵌套,那么浅拷贝将无能为力
实现浅拷贝
const shallowClone = (obj) => {
if (typeof obj === 'object' && obj != null) {
const cloneObj = Array.isArray() ? [] : {}
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
cloneObj[prop] = obj[prop]
}
}
return cloneTarget
}
return target
}
深拷贝
递归
function deepClone(obj) {
let cloneObj = {}
for(let key in obj) {
if(typeof obj[key] === 'object') {
cloneObj[key] = deepClone(obj[key])
} else {
cloneObj[key] = obj[key]
}
}
return cloneObj
}
虽然利用递归能实现以下深拷贝,但是同JSON.stringfy一样,还是有一些问题没有解决,例如:
这个深拷贝函数并不能复制不可枚举的属性以及Symbol类型
针对不能复制不可枚举类型以及Symbol类型,我们可以使用Reflect.ownKeys方法
这种方法只是针对普通的引用类型的值做递归复制,而对Array, Date, RegExp, Error, Function这样的引用类型并不能正确的拷贝
当参数为Date, RegExp类型,直接生成一个新的实例返回 利用Object的getOwnPropertyDescriptors方法可以获得对象的所有属性,以及对应的特性 顺便结合Object.create创建一个新对象,并传入原对象的原型链
对象的属性里面成环,即循环引用没有解决
利用WeakMap作为Hash表,因为WeakMap是弱引用类型,可以有效防止内存泄露,作为检测循环引用很有帮助,如果存在循环,则引用直接返回WeakMap存储的值
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)