浅拷贝
Object.assign
object.assign 的语法为:Object.assign(target, ...sources)
Object.assign
特点
- 它不会拷贝对象的继承属性;
- 它不会拷贝对象的不可枚举的属性;
-
能够拷贝 Symbol 类型的属性。
扩大运算符形式
扩大运算符的语法为:let cloneObj = {...obj};
扩大运算符拷贝形式和
Object.assign
相似,都只是进行浅拷贝,如果属性都是根本类型的值,应用扩大运算符进行浅拷贝会很不便。concat 拷贝数组
let arr = [1,2]; let newArr = arr.concat(); newArr[1] = 10; console.log(arr); // [1, 2] console.log(newArr); // [1, 10]
应用场景有局限性
slice 拷贝数组
slice 的语法为:arr.slice(begin, end);
会返回一个新数组,不扭转原数组,只能拷贝一层对象,嵌套类型无能为力。
实现一个浅拷贝
思路如下
- 对根底类型做一个最根本的一个拷贝;
- 对援用类型开拓一个新的存储,并且拷贝一层对象属性。
function shallowClone(target) {if (typeof target === object && target !== null) {const clone = Array.isArray(target) ? [] : {}
for (let i in target) {if (target.hasOwnProperty(i)) {clone[i] = target[i]
}
}
return clone;
} else {return target}
}
深拷贝
深拷贝原理:将一个对象从内存中残缺地拷贝进去一份给指标对象,并从堆内存中开拓一个全新的空间寄存新对象,且新对象的批改并不会扭转原对象,二者实现真正拆散
JSON.stringify
这种形式比拟常见,应用也比较简单
let obj1 = {a:1, b:[1,2,3] }
let str = JSON.stringify(obj1);let obj2 = JSON.parse(str);
存在缺点
- 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,通过 JSON.stringify 序列化之后的字符串中这个键值对会隐没;
- 拷贝 Date 援用类型会变成字符串;
- 无奈拷贝不可枚举的属性;
- 无奈拷贝对象的原型链;
- 拷贝 RegExp 援用类型会变成空对象;
- 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的后果会变成 null;
- 无奈拷贝对象的循环利用,即对象成环 (obj[key] = obj)。
递归形式拷贝
理论利用中比拟常见,这种形式应用也比拟频繁
function deepClone(obj) {let clone = {};
for (let key in obj) {if (typeof obj[key] === "object") {clone[key] = deepClone(obj[key])
} else {clone[key] = obj[key]
}
}
return clone;
}
存在问题
- 这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
- 这种办法只是针对一般的援用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的援用类型并不能正确地拷贝;
- 对象的属性外面成环,即循环援用没有解决。
改良版本深拷贝
针对以上拷贝存在的问题。
改良版本新增解决项
- 针对可能遍历对象的不可枚举属性以及 Symbol 类型,咱们能够应用 Reflect.ownKeys 办法;
- 当参数为 Date、RegExp 类型,则间接生成一个新的实例返回;
- 利用 Object 的 getOwnPropertyDescriptors 办法能够取得对象的所有属性,以及对应的个性,顺便联合 Object 的 create 办法创立一个新对象,并继承传入原对象的原型链;
- 利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱援用类型,能够无效避免内存透露(你能够关注一下 Map 和 weakMap 的要害区别,这里要用 weakMap),作为检测循环援用很有帮忙,如果存在循环,则援用间接返回 WeakMap 存储的值。
Reflect.ownKeys 办法返回一个由指标对象本身的属性键组成的数组。它的返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。Object.getOwnPropertyDescriptors。这个办法次要的作用是返回属性的形容对象(descriptor)WeakMap 和 Map 相似,然而有区别:1、WeakMap 只承受对象作为 key,如果设置其余类型的数据作为 key,会报错。2、WeakMap 的 key 所援用的对象都是弱援用,只有对象的其余援用被删除,垃圾回收机制就会开释该对象占用的内存,从而防止内存透露。3、因为 WeakMap 的成员随时可能被垃圾回收机制回收,成员的数量不稳固,所以没有 size 属性。4、没有 clear()办法
5、不能遍历
代码实现
// 判断是否是简单数据类型
const isComplexDataType = obj => {(typeof obj === "object" || typeof obj === "function") && (obj !== null)
}
const deepClone = function (obj, hash = new WeakMap()) {
// 非凡对象解决
if (obj.constructor === Date)
return new Date(obj)
if (obj.constructor === RegExp)
return new RegExp(obj)
// 如果循环援用了就用 WeakMap 来解决
if (hash.get(obj))
return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj);
// 遍历传入参数所有键的个性
let clone = Object.create(Object.getPrototypeOf(obj), allDesc)
// 继承原型链
hash.set(obj, clone)
for (let key of Reflect.ownKeys(boj)) {clone[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== "function") ? deepClone(obj[key], hash) : obj[key]
}
return clone
}