面试题-深拷贝(超高频)-包含各种对象类型

266 阅读2分钟

深拷贝

  • 赋值:和原数据指向同一对象,改变会使原数据改变
  • 浅拷贝:和原数据不指向同一对象,改变基本数据类型不会影响原数据,改变引用数据类型会影响原数据
  • 深拷贝:和原数据不指向同一对象,改变任何数据类型都不会影响原数据

JSON实现深拷贝

  • JSON.parse(JSON.stringify(obj))
  • 缺点:
    • 无法拷贝函数等实例对象类型
    • 无法拷贝copyObj对象原型链上的属性和方法
    • 当数据的层次很深,会栈溢出

自定义递归函数

  • 废话不多说,先定义一个包含各种类型的对象,就是我们要拷贝到源对象

    const origin =  {
      name: 'zzy', // string
      age: 22, // number
      isMarry: false, // boolean
      emotion: undefined, // undefined
      [Symbol('key')]: 'SymValue', // symbol
      wealth: BigInt(10000), // bigInt
      rank: null, // null
      friends: {
        name: 'lll',
        books: ['css', 'js', 'html']
      }, // object
      tele: ['112', '110', '119'], // array
      run: function () {
        console.log(this.name, '在跑步')
      }, //function
      reg: new RegExp('^[d]{3-5}$', 'g'), // RegExp
      date: new Date(), // Date
      dict: new Map([
        [true, 'value2'],
        [{ name: 'mapChild' }, 23]
      ]), // map
      set: new Set([1, 2, 3]) // set
    }
    
  • 下面就开始写拷贝函数,针对不同类型数据进行不同处理,需要注意对象类型包括很多种,有的需要重新构造,有的需要递归处理

    const deepClone = (target, hash = new WeakMap()) => {
       // 基本数据类型,直接返回
       if (typeof target !== 'object' && target !== null) {
         return target
       }
       // null类型
       if (target === null) {
         return null
       }
       // function类型,复用同一个
       if (typeof target === 'function') {
         return target
       }
       // map类型
       if (target instanceof Map) {
         return new Map([...target])
       }
       // set类型
       if (target instanceof Set) {
         return new Set([...target])
       }
       // symbol类型
       if (typeof target === 'symbol') {
         return Symbol(target.description)
       }
       // RegExp
       if (target instanceof RegExp) {
         const Cscr = target.constructor
         return new Cscr(target)
       }
       // Date
       if (target instanceof Date) {
         const newDate = new Date()
         newDate.setTime(target.getTime())
         return newDate
       }
       // 判断是否重复拷贝
       if (hash.has(target)) {
         return hash.get(target)
       }
       // 判断是否为对象
       let result = Array.isArray(target) ? [] : {}
       hash.set(target, result)
       // Symbols
       const symKeys = Object.getOwnPropertySymbols(target)
       // 返回的是一个数组
       if (symKeys.length) {
         symKeys.forEach((sK) => {
           result[sK] =
             typeof target[sK] === 'object' && target !== null
               ? deepClone(target[sK])
               : target[sK]
         })
       }
       // Object
       for (let key in target) {
         // 忽略原型上的属性
         if (target.hasOwnProperty(key)) {
           result[key] =
             typeof target[key] === 'object' && target !== null
               ? deepClone(target[key])
               : target[key]
         }
       }
       return result
     }
    
  • 测试是否拷贝成功

        const copyObj = deepClone(origin)
        // 修改拷贝出来的对象的各种属性,如果源对象的属性没有被改变,则代表拷贝成功
        copyObj.name="robot"
        copyObj.friends.name="zzz"
        copyObj.friends.books.push('react')
        copyObj.tele.push('12315')
        copyObj.dict.set('newMap','newVal')
        copyObj.set.delete(2)
        copyObj.rank = '王者'
        copyObj.wealth = BigInt(100000000)
        copyObj.emotion = 'happy'
        copyObj.run()
        console.log(origin)
        console.log(copyObj)
    

    image.png