浅拷贝与深拷贝

76 阅读1分钟

浅拷贝与深拷贝

浅拷贝

  1. Object.assign()Object.getPrototypeOf(),Object.getOwnPropertyDescriptors()

    Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))
    
  2. 使用对象的展开语法 let cloneObj = { ...obj }

  3. 对数组使用 concatslice 方法

深拷贝

  1. 通过 JSON.stringify 转实现

    • 不能对DateFunctionSymbolMapSet等类型进行拷贝
    • 循环引用会出现问题
    • 对不可枚举属性,对象的原型链等无法拷贝
    JSON.parse(JSON.stringify())
    
  2. 手动实现深拷贝

    • 使用WeakMap处理循环引用
    • 单独拷贝 Map,Set,Date,Function,RegExp等类型
    • 使用 Reflect.ownKeys() 获取目标对象自身的属性
    • 使用 Object.getOwnPropertyDescriptors 获取对象的所有自身属性的描述符 和 Object.getPrototypeOf() 返回对象的原型,获取对象的浅拷贝(包括原型)
    function deepClone(target) {
      // weakMap 防止循环引用
      const map = new WeakMap()
      // obj
      function isObject(target) {
        return (
          (typeof target === 'object' && target) || typeof target === 'function'
        )
      }
    
      function clone(data) {
        // 基础类型
        if (!isObject(data)) {
          return data
        }
        // 日期 正则对象
        if ([Date, RegExp].includes(data.constructor)) {
          return new data.constructor(data)
        }
        // function
        if (typeof data === 'function') {
          return new Function('return ' + data.toString())()
        }
    
        // 对象已存在则直接返回
        const exit = map.get(data)
        if (exit) {
          return exit
        }
    
        // Map
        if (data instanceof Map) {
          const result = new Map()
          data.forEach((key, value) => {
            if (isObject(value)) {
              result.set(key, clone(value))
            } else {
              result.set(key, value)
            }
          })
          map.set(data, result)
          return result
        }
    
        // Set
        if (data instanceof Set) {
          const result = new Set()
          data.forEach((value) => {
            if (isObject(value)) {
              result.add(clone(value))
            } else {
              result.add(value)
            }
          })
          map.set(data, result)
          return result
        }
        if (Array.isArray(data)) {
          const result = []
          data.forEach((item) => {
            if (isObject(item)) {
              result.push(clone(item))
            } else {
              result.push(item)
            }
          })
          map.set(data, result)
          return result
        }
        // 收集键名
        const keys = Reflect.ownKeys(data)
        const allDesc = Object.getOwnPropertyDescriptors(data)
        const result = Object.create(Object.getPrototypeOf(data, allDesc))
    
        keys.forEach((key) => {
          const value = data[key]
    
          if (isObject(value)) {
            result[key] = clone(value)
          } else {
            result[key] = value
          }
        })
        map.set(data, result)
        return result
      }
    
      return clone(target)
    }