js深度拷贝

352 阅读1分钟

一般实现方式有两种:

  1. JSON.parse(JSON.stringify(obj)),用法说明参考developer.mozilla.org/zh-CN/docs/…

    注意点,不可枚举undefined函数symbol是不能stringify的,另外循环引用会报错,如下:

可以通过一下方式处理: (使用json.stringify的第二个参数)

var cache = []
JSON.stringify(json, (key, value) => {
    if (typeof value === 'object' && value !== null) {
        if (cache.indexOf(value) !== -1) {
            return // 或者 return undefined
        }
        cache.push(value)
    }
    return value;
})
  1. 直接实现:

    从上面的stringify的用法,就会发现弊端,如无法拷贝函数引用等等以及循环引用的bug,不过处理循环无污染的方式是用一个数组记录(污染的方式是对每一个对象添加一个__used的属性标记改对象已被使用,无需再调用赋值),具体代码如下:

function deepClone(obj) {
    const rootObj = {} // 记录新的对象
    const cache = [obj] // 引用对象数组
    const cacheNewValues = [rootObj] // 记录对应引用的新值

    const assign = (innerObj) => {
      const newObj = innerObj === obj ? rootObj : {}
      Object.keys(innerObj).forEach(key => {
        const value = innerObj[key]
        const index = cache.indexOf(value)
        // 未缓存
        if(index === -1) {
          const type = Object.prototype.toString.call(value)
          if (typeof type === 'object') {
            newObj[key] = assign(value)
            // 记录引用的对象以及对应引用的新值
            cache.push(value)
            cacheNewValues.push(newObj[key])
          } else {
            newObj[key] = value
          }
        } else {
          // 已缓存,取对应缓存的新值
          newObj[key] = cacheNewValues[index]
        }
      })
      
      return newObj
    }

    return assign(obj);
};

运行如下:

deep-clone

思考: 栈溢出了呢? 下次更新这个问题