js基础之深浅拷贝

146 阅读2分钟

概念

首先来区分几个概念:赋值,浅拷贝和深拷贝

赋值

对于基本类型,就是直接把值 赋给变量, 对于引用类型就是把引用地址赋给变量

    let obj = {x: 1}
    let obj1 = obj
    console.log(obj === obj1) // true

浅拷贝

浅拷贝是对于引用类型来说的, 如果对象里的属性值是基本类型, 浅拷贝就是复制该属性值的值, 对于属性值是引用类型的, 就复制该值的引用地址,常见的浅拷贝:

    let obj = {x: 1, y: {c: 666}}
    let obj1 = {...obj}
    console.log(obj === obj1)      // false
    console.log(obj.y === obj1.y) // true

深拷贝

深拷贝是直接开辟一个空间存放要拷贝的对象,两个对象对应不同应用地址 常用乞丐版深拷贝:

  let obj = {x: 1, y: {c: 999}}
  let obj1 = JSON.parse(JSON.stringify(obj))
  console.log(obj === obj1)      // false
  console.log(obj.y === obj1.y) // false

但是这样的深拷只能拷贝对象和数组,对于函数,undefined,null,symbol之类的会有问题, 而且还有所拷贝的对象中不能有循环引用, 否则会报错。

实现深浅拷贝

// 测试用例
let obj = {
   0: 'math',
   1: 'chinese',
   2: 'elglish',
   score: {
       math: 98,
       chinese: 100,
       elglish: 19,
   },
   reg: /\d+/img,
   time: new Date,
   friends: ['tom', 'jerry'],
   say: function () {
       console.log('good good study!');
   },
   tag: Symbol('TAG'),
   [Symbol.toStringTag]: 'object'
};

// 获取对象类型
function getType(obj) {
 return Object.toString.call(obj).slice(8, -1)
}

浅拷贝

function shallowCopy (obj) {
   let Ctor = obj.constructor
   let type = getType(obj)
   // 1. 对于symbol和bigInt
   if(/^(symbol|bigint)&/i.test(type)) return Object(obj)
   // 2. 对于是error
   if(/^error$/i.test(type)) return new Ctor(obj.message)
   // 3. regexp/ date
   if(/^(regexp|date)$/i.test(type)) return new Ctor(obj)
   // 4. function // 返回新函数:新函数执行还是把原始函数执行,实现和原始函数相同的效果
   if(/^function$/i.test(type)) {
       return function () {
         return obj.call(this, ...arguments)
       }
   }
   // 5. object and array
   if(/^(object|array)$/i.test(type)){
       // 获取键
       let keys = [
             ...Object.keys(obj),
             ...Object.getOwnPropertySymbols(obj)
       ]
       let newObj = new Ctor()
       for(let key of keys) {
            newObj[key] = obj[key]
       }
       return newObj
   }
   return obj
}

深拷贝

function deepCopy (obj, set = new Set()) {
   let Ctor = obj.constructor
   let type = getType(obj)
   if(!/^(array|object)$/i.test(type)) return shallowCopy(obj)
   if(set.has(obj)) return obj   // 防止循环引用
   set.add(obj)
   let keys = [
       ...Object.keys(obj),
       ...Object.getOwnPropertySymbols(obj)
   ]
   let result = new Ctor()
   for(let key of keys){
       result[key] = deepCopy(obj[key], set)
   }
   return result
}

测试

let obj1 = shallowCopy(obj)
let obj2 = deepCopy(obj)
console.log(obj === obj1) // false
console.log(obj === obj2) // false
console.log(obj.score === obj1.score) // true
console.log(obj.score === obj2.score) // false
obj.friends.push('shetia')
console.log(obj1, '---------------obj1')
console.log(obj2, '---------------obj2')

image.png