浅拷贝

Object.assign

object.assign 的语法为:Object.assign(target, ...sources)

Object.assign特点

  1. 它不会拷贝对象的继承属性;
  2. 它不会拷贝对象的不可枚举的属性;
  3. 能够拷贝 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);

    会返回一个新数组,不扭转原数组,只能拷贝一层对象,嵌套类型无能为力。

实现一个浅拷贝
思路如下

  1. 对根底类型做一个最根本的一个拷贝;
  2. 对援用类型开拓一个新的存储,并且拷贝一层对象属性。
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);

存在缺点

  1. 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,通过 JSON.stringify 序列化之后的字符串中这个键值对会隐没;
  2. 拷贝 Date 援用类型会变成字符串;
  3. 无奈拷贝不可枚举的属性;
  4. 无奈拷贝对象的原型链;
  5. 拷贝 RegExp 援用类型会变成空对象;
  6. 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的后果会变成 null;
  7. 无奈拷贝对象的循环利用,即对象成环 (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;}

存在问题

  1. 这个深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
  2. 这种办法只是针对一般的援用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的援用类型并不能正确地拷贝;
  3. 对象的属性外面成环,即循环援用没有解决。

改良版本深拷贝

针对以上拷贝存在的问题。
改良版本新增解决项

  1. 针对可能遍历对象的不可枚举属性以及 Symbol 类型,咱们能够应用 Reflect.ownKeys 办法;
  2. 当参数为 Date、RegExp 类型,则间接生成一个新的实例返回;
  3. 利用 Object 的 getOwnPropertyDescriptors 办法能够取得对象的所有属性,以及对应的个性,顺便联合 Object 的 create 办法创立一个新对象,并继承传入原对象的原型链;
  4. 利用 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 }