美文网首页
JavaScript 之深浅拷贝

JavaScript 之深浅拷贝

作者: 临安linan | 来源:发表于2019-11-08 20:03 被阅读0次

    更多个人博客:(https://github.com/zenglinan/blog)

    如果对你有帮助,欢迎star。

    对象拷贝是经常会遇到的场景, 本文会总结几种深浅拷贝的方法

    一. 浅拷贝

    1. 浅拷贝的定义

    先来说说什么是浅拷贝, 首先要明确一点:

    直接拷贝对象的引用这不叫浅拷贝

    先来看一个案例:

    拷贝引用:

    var obj1 = {
      a: 1,
      b: {
        c: 1
      }
    }
    
    var obj2 = obj1  // 拷贝 obj1 的引用
    
    obj2.a = 2
    console.log(obj1.a)  // 2
    

    浅拷贝:

    var obj1 = {
      a: 1,
      b: {
        c: 1
      }
    }
    
    var obj2 = {...obj1}
    
    obj2.a = 2
    console.log(obj1.a)  // 1
    

    上面可以看到, 直接复制引用时, 两个对象实际保存的是堆中同一个对象的引用, 而浅拷贝则不同, 浅拷贝重新生成了一个新的对象, 但是浅拷贝只会拷贝一层, 假如原对象的属性值为对象, 也只会拷贝这个对象的引用。

    也就是说: 在浅拷贝的对象中, 修改基本类型的值不会影响原数据, 修改复杂类型时, 会影响原数据

    2. 浅拷贝的实现

    (1) ...扩展运算符

    var copy = {...obj} 
    

    ES6 中提供了扩展运算符, 可以遍历取出对象身上的属性, 分配到新的对象上

    (2) Object.assign

    var copy = {}
    Object.assign(copy, obj)
    

    Object.assign 可以将第二个对象里可枚举的属性复制到第一个对象里

    (3) for...in

    function shallowCopy(src){
      let result = {}
      for(let prop in src){
        if(src.hasOwnProperty(prop)){
          result[prop] = src[prop]
        }
      }
      return result
    }
    

    注意: for...in 循环会遍历原型链上所有的的属性, 浅拷贝只需拷贝对象身上的属性。

    所以加一层 hasOwnProperty 判断

    二. 深拷贝

    1. 深拷贝的定义

    与浅拷贝相比, 深拷贝也会对对象的子对象进行拷贝

    当修改拷贝对象上的对象属性时, 不会对源对象产生影响

    2. 深拷贝的实现

    (1) JSON序列化与反序列化

    最先说的办法当然是家喻户晓的 JSON 序列化与反序列化方法啦~

    var copy = JSON.parse(JSON.stringify(src))
    

    先序列化一下对象变成字符串, 然后再反序列化成对象

    这个办法有两个缺陷:

    1. 既然是 JSON 对象上的方法, 当然不支持拷贝函数了o(╥﹏╥)o

    2. 同样的道理, JSON 里面没有 Symbol 类型, 自然也不支持 Symbol 类型的键

    但是这个方法最简单了, 一行代码...

    (2) for...in + 递归

    function deepClone(src){
      if(typeof src !== "object") return src
    
      let result = Array.isArray(src) ? [] : {}
    
      for(let prop in src){
        if(!src.hasOwnProperty(prop)) continue
        let value = src[prop]
        result[prop] = typeof value === 'object' ? deepClone(value) : value
      }
      return result
    }
    

    这个方法比 JSON 序列化的方法更完善的一点是: 支持拷贝函数

    但是依然无法拷贝键为 Symbol 类型的属性

    原因是: for...in 无法遍历到 Symbol 类型的属性

    (3) Reflect.ownKeys() + 递归

    MDN 上是这样描述的:

    Reflect.ownKeys 方法返回一个由目标对象自身的属性键组成的数组。它的返回值等同于

    Object.getOwnPropertyNames(target)
    .concat(Object.getOwnPropertySymbols(target))
    

    这个方法可以取到对象自身的所有属性, 包括 Symbol 类型的属性

    function deepClone(src){
      if(typeof src !== 'object') return src
    
      let result = Array.isArray(src) ? [] : {} 
      Reflect.ownKeys(src).forEach((key) => {
        let value = src[key]
        result[key] = typeof value === 'object' ? deepClone(value) : value
      })
      return result
    }
    

    相关文章

      网友评论

          本文标题:JavaScript 之深浅拷贝

          本文链接:https://www.haomeiwen.com/subject/cliwbctx.html