当前位置: 移动技术网 > IT编程>开发语言>JavaScript > JS: 深拷贝

JS: 深拷贝

2019年04月17日  | 移动技术网IT编程  | 我要评论

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

对于深拷贝,我平时用得很少,一般都是用 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 改成数组。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网