zoukankan      html  css  js  c++  java
  • JS: 深拷贝

    注意:以下深拷贝仅针对对象。

    对于深拷贝,我平时用得很少,一般都是用 JSON 的方法来实现:

    let newObj = JSON.parse(JSON.stringify(oldObj))
    

    但前几天踩了坑,在网上查了才发现问题,只能说坑只有踩过才知道深浅。


    1. 对于 function、undefined,会丢失这些属性。

    2. 对于 RegExp、Error 对象,只会得到空对象

    3. 对于 date 对象,得到的结果是 string,而不是 date 对象

    4. 对于 NaN、Infinity、-Infinity,会变成 null

      let oldObj = {
        test1: null,
        test2: undefined,
        fn: () => {console.log('fn')},
        date: new Date(),
        RegExp: /(a|b)/g,
        Error: new Error('err'),
        NaN: Number('NaN')
      }
      
      let newObj = JSON.parse(JSON.stringify(oldObj))
      
      // 丢失 function、undefined
      // Error、RegExp 为空对象
      // NaN 变为 null
      // date 对象变为 string
      newObj
      /*
      {
      	Error: {}
      	RegExp: {}
      	date: "2019-04-16T11:43:05.870Z"
      	NaN: null
      	test1: null
      }
      */
      
    5. 无法处理循环引用

      let oldObj = { }
      oldObj.obj = oldObj
      
      // 会报错
      let newObj = JSON.parse(JSON.stringify(oldObj))
      // TypeError: Converting circular structure to JSON
      

    浅拷贝


    浅拷贝方法还是挺多的,列举一二:

    1. Object.assign()

      let oldObj = {
        name: 'parent',
        children: 'children'
      }
      
      let newObj = Object.assign({}, oldObj)
      
    2. 循环

      /*
       * 浅拷贝,仅针对对象
       * params {object} obj
       */
      function shallowCopy (obj) {
        if (Object.prototype.toString.call(obj) !== '[object Object]') {
          throw new TypeError(`${obj} is not a object`)
        }
        
        let res = {}
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
      			res[key] = obj[key]
          }
        }
        
        return res
      }
      

    深拷贝


    深拷贝的实现也是可以使用for...in + 递归实现的:

    function isObject (obj) {
      return Object.prototype.toString.call(obj) === '[object Object]'
    }
    
    /*
     * 深拷贝,仅针对对象
     * params {object} obj
     */
    function deepCopy (obj) {
      if (!isObject(obj)) {
        throw new TypeError(`${obj} is not a object`)
      }
      
      let res = {}
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          res[key] = isObject(obj[key]) ? deepCopy(obj[key]) : obj[key]
        }
      }
      
      return res
    }
    

    虽然解决了大部分JSON.parse(JSON.stringify(oldObj))的问题,但依然无法解决循环引用的问题。

    let oldObj = {}
    oldObj.obj = oldObj
    
    let newObj = deepCopy(oldObj)
    
    newObj.obj === oldObj.obj // true
    

    解决循环引用


    其实只要将已被拷贝的对象存储下来,每次递归之前都检查一遍该对象是否已经被拷贝,就可以解决循环引用的问题了。

    /*
     * 深拷贝,仅针对对象
     * params {object} obj
     */
    function deepCopy (obj, list = new WeakMap()) {
      if (list.has(obj)) {
        return list.get(obj)
      }
      
      if (!isObject(obj)) {
        throw new TypeError(`${obj} is not a object`)
      }
      
      let res = {}
      list.set(obj, res)
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          res[key] = isObject(obj[key]) ? deepCopy(obj[key], list) : obj[key]
        }
      }
      
      return res
    }
    

    这样循环引用的问题就解决啦,是不是很简单。

    let oldObj = {}
    oldObj.obj = oldObj
    
    let newObj = deepCopy(oldObj)
    
    newObj.obj === oldObj.obj // false
    

    当然,WeakMap 可能会存在兼容性问题,所以可以将 list 改成数组。

  • 相关阅读:
    bzoj4165 矩阵 堆维护多路归并
    bzoj2802 [Poi2012]Warehouse Store 贪心+堆
    bzoj1367 [Baltic2004]sequence 左偏树+贪心
    bzoj3011 [Usaco2012 Dec]Running Away From the Barn 左偏树
    uoj207 共价大爷游长沙 子树信息 LCT + 随机化 + 路径覆盖
    bzoj4764 弹飞大爷 LCT
    bzoj4817 & loj2001 [Sdoi2017]树点涂色 LCT + 线段树
    bzoj5020 & loj2289 [THUWC 2017]在美妙的数学王国中畅游 LCT + 泰勒展开
    bzoj4998 星球联盟 LCT + 并查集
    bzoj3091 城市旅行 LCT + 区间合并
  • 原文地址:https://www.cnblogs.com/guolao/p/10720069.html
Copyright © 2011-2022 走看看