copy深拷贝、merge合并的终极探究

267 阅读1分钟

拷贝

//判断数据类型

function toType(obj){
  let classType = {},
  toString = classType.toString // => Object.prototype.toString 
  // 设置数据类型映射
  let typeArr = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error', 'Symbol']
  typeArr.forEach(name => {
    classType[`[object ${name}]`] = name.toLowerCase()
  })

  if(obj == null) return obj + '' // 检测null、undefined
  return typeof obj === 'object' || typeof obj === 'function' ? classType[toString.call(obj)] || 'object' : typeof obj
}

//对象和数组的遍历

function each(obj, callback){
  if (toType(obj) === 'array') {
      for (let i = 0; i < obj.length; i++) {
          callback(obj[i], i)
      }
  }
  if (toType(obj) === 'object') {
      for (const key in obj) {
        console.log('key:', key);

          callback(obj[key], key)
      }
  }
  return obj
}

//对象的浅拷贝

function objectAndArrayShallowClone(obj){
  // {...obj} // value值为null或者undefined时属性会丢失
  let newObj = new obj.constructor
  let keys = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj))

  each(keys, item => {
      newObj[item] = obj[item]
  })

  return newObj
}

// 浅克隆

function shallowClone(obj){
  let type = toType(obj),
    Ctor = obj.constructor; //当前对象的构造函数
    //Symbol
    if(/^(symbol)$/i.test(type)) return Object(obj)
    // RegExp Date
    if(/^(regexp|date)$/i.test(type)) return new Ctor(obj)
    // 错误类型
    if(/^(error)$/i.test(type)) return new Error(obj.message)
    //对于函数
    if (/^function$/i.test(type)) {
      return function(){
        return obj.call(this, ...arguments)
      }
    }
    // 数组或者对象
    if(/^(object|array)$/i.test(type)){ //是数组或者对象
      let result = objectAndArrayShallowClone(obj)
      return result
    }
    //基础数据类型
    return obj
}

//深克隆:只要有下一级 我们就克隆一次(浅克隆)

function deepClone(obj, cache= new Set()){ // cache 套娃深拷贝的处理 obj = {0: obj}
  if (!obj) { // null\undefined
    return obj
  }
  let type = toType(obj),
    Ctor = obj.constructor;    
    if (!/^(object|array)$/i.test(type)) return shallowClone(obj)

    //避免无限套娃
    if (cache.has(obj)) return obj
    cache.push(obj)

    let newObj = new Ctor(),
    keys = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj))

    each(keys, item => {
      //再次调用deepClone的时候,把cache传递进去,保证每一次递归都是一个cache
        newObj[item] = deepClone(obj[item], cache)
    })
    return newObj
}

merge

let options = Object.assign(oldOptions, newOptions)

let options = {...oldOptions, ...newOptions}

上面两种方法不足:相同类型的同名属性 后者直接覆盖前者

正确合并

  • 几种情况的分析

  • A-> obj中的key值, B-> target中的key值

  • 1、A&B都是原始值,B替换A即可

  • 2、A是对象 B是原始值 抛出异常信息

  • 3、A是原始值 B是对象 B替换A即可

  • 4、A&B都是对象:依次遍历B中的每一项,替换A中的内容

     function merge(obj, target={}){
       each(target, (_,key) => {
         if (isObj(obj[key]) && !isObj(target[key])) throw new Error(`${key} in target must be object`)
         if (isObj(obj[key]) && isObj(target[key])){
           obj[key] = merge(obj[key], target[key])
           return
         } 
         obj[key] = target[key]
       })
       return obj
     }
    
     function isObj(val){
       return toType(val) === 'object'
     }