JS基础总结 - 深浅拷贝

  • 浅拷贝:以对象为例,如果对象的属性是值类型,拷贝的是这个值类型的值;如果是引用类型,拷贝的就是内存地址。
  • 深拷贝:将一个对象从内存中完整的拷贝一份出来。还是以对象为例,如果对象的属性是值类型,拷贝这个值到栈中;如果是引用类型,那么就在堆内存中开辟一个新的区域存放这个引用类型的原始对象(而不是内存地址)。这样对新对象的修改,不会影响到原对象。

浅拷贝:

  • 测试对象

const obj1 = {
    name: 'xxx',
    age: 20,
    address: {
        city: '北京'
    },
    arr: ['a', 'b', 'c']
}
  • 实现方式

// 方法一: 遍历
function simpleClone (oldObj) {
  let newObj = {}
  for(const i in oldObj) {
    newObj[i] = oldObj[i]
  }
  return newObj
}

// 方法二: Object.creat()
let obj2 = Object.create(obj1)

// 方法三:Object.assign()
let obj2 = Object.assign({}, obj1)

// 方法四: 直接拷贝
// * 这种方式,即使属性是基本类型,obj1 和 obj2 也会相互影响
let obj2 = obj1

// 测试
obj2.address.city = '上海'
consloe.log(obj1.address.city) // 上海

深拷贝

  • 基础版

    缺点: 不支持 undefined、function类型

    const obj1 = {
      a: 'aaa',
      b: null,
      c: [],
      d: {},
      c: undefined,
      d: function() {}
    }
    const obj2 = JSON.parse(JSON.stringify(obj1))
    console.log(obj2) // -> { a:'aaa', b:null, c:[], d:{}}  // undefined && function 没有被拷贝
    
  • 升级版

    缺点: 不支持 循环引用

    function clone(target) {
      if (typeof target !== 'object' || target == null) {
        // ↑ == 用于兼并判断 undefined,这里没必要因为第一个条件已判断,但这也是一种编程小技巧,所以还是用 ==
        return target
      }
      let result = Array.isArray(target) ? [] : {}
      for (const key in target) {
        if (target.hasOwnProperty(key)) { // 保证 key 不是原型属性
          result[key] = clone(target[key])
        }
      }
      return result
    }
    
  • 加强版

    缺点: 没考虑性能优化

    function clone(target, map = new Map()) {
      if (typeof target !== 'object' || target == null) {
        return target
      }
      if (map.get(target)) {
        return map.get(target)
      }
      let result = Array.isArray(target) ? [] : {}
      map.set(target, result)
      for (const key in target) {
        result[key] = clone(target[key], map)
      }
      return result
    }
    
    // 测试
    const target = {
      field1: 1,
      field2: undefined,
      field3: {
          child: 'child'
      },
      field4: [2, 4, 8]
    };
    target.target = target
    const test = clone(target)
    console.log(test)
    
  • 终极版

    使用 WeakMap 进行垃圾回收, 完美!!! ღゝ◡╹)ノ♡

    WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象( Object 类型),而值可以是任意的。

    WeakMap 持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行。原生 WeakMap 的结构是特殊且有效的,其用于映射的 key 只有在其没有被回收时才是有效的。

    由于这样的弱引用,WeakMap 的 key 是不可枚举的 (没有方法能给出所有的 key)。如果key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,你应该使用 Map

    以下代码帮你理解,WeakMap:

    // Map
    let key = { name : 'ConardLi'}
    const target = new Map()
    target.set(key, 'hello')
    key = null
    // * 虽然我们手动将 key ,进行释放,但 target 依然对 key 存在强引用关系,所以这部分内存无法被释放。
    
    // WeakMap
    let key = { name : 'ConardLi'}
    const target = new WeakMap()
    target.set(key, 'code秘密花园')
    key = null
    // * target 和 obj 存在的就是弱引用关系,当下一次垃圾回收机制执行时,这块内存就会被释放掉。
    

    终极版代码

    function clone(target, map = new WeakMap()) {
      if (typeof target !== 'object' || target == null) {
        return target
      }
      if (map.get(target)) {
        return map.get(target)
      }
      let result = Array.isArray(target) ? [] : {}
      map.set(target, result)
      for (const key in target) {
        result[key] = clone(target[key], map)
      }
      return result
    }
    
    // 测试
    const target = {
      field1: 1,
      field2: undefined,
      field3: {
          child: 'child'
      },
      field4: [2, 4, 8]
    };
    target.target = target
    const test = clone(target)
    console.log(test)
    
posted @ 2021-02-28 20:46  Better-HTQ  阅读(59)  评论(0编辑  收藏  举报