• 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 改成数组。

  • 相关阅读:
    java 多线程2
    java 多线程1
    java Object
    java 模板
    03015_JSTL技术
    运维人员常用的Linux命令总结
    oracle的userenv和nls_lang详解
    Oracle字符集检查和修改
    sql语句中(+)的作用
    Oracle事务Transaction
  • 原文地址:https://www.cnblogs.com/guolao/p/10720069.html
Copyright © 2020-2023  润新知