面试(五)之浅拷贝、深拷贝

114 阅读2分钟

摘录于浅拷贝与深拷贝

浅拷贝

如果是基本类型,拷贝的是基本类型的值;
如果是引用类型,拷贝的是内存地址。所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
浅拷贝只复制指向某个对象的指针,而不复制对象本身。

深拷贝

从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
新旧对象不共享内存。

浅拷贝方式(5)

  1. Object.assign()

把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象

  1. lodash的 _.clone
  2. 展开运算符. . .
let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }
  1. Array.prototype.concat()
let arr = [1, 3, { username: 'kobe' }];
let arr2 = arr.concat();
arr2[0] = 'wade';
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]
console.log(arr2); //[ 'wade', 3, { username: 'wade' } ]
  1. Array.prototype.slice()

同 展开运算符 同 Array.prototype.concat()

深拷贝方式(4)

  1. JSON.parse(JSON.stringify())

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则。因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。

  1. lodash的_.cloneDeep
  2. jQuery.extend()
  3. 手写递归方法 原理:遍历对象、数组直到里面都是基本数据类型,然后再去复制,就是深度拷贝。
    有些特殊情况需注意就是对象存在循环引用,即对象的属性直接引用了自身的情况。
    解决循环引用问题,可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系。当需要拷贝当前对象时,先去存储空间找。有没有拷贝过这个对象,如果有的话,直接返回;如果没有的话继续拷贝,这样就巧妙化解循环引用的问题。
function deepClone(obj, hash = new WeakMap()) {
    if (obj === null) return obj // 如果是null或者undefined就不拷贝
    if (obj instanceof Date) return new Date(obj)
    if (obj instanceof RegExp) return new RegExp(obj)
    // 可能是对象或者普通值 
    if (typeof obj !== 'object') return obj
    // 是对象深拷贝
    if (hash.get(obj)) return hash.get(obj)
    let cloneObj = new obj.constructor()
    // 找到的是所属类原型上的constructor,而原型上的constructor指向的是当前类本身
    hash.set(obj, cloneObj)
    for (let key in obj) {
     if (obj.hasOwnProperty(key)) {
         // 实现一个递归拷贝
         cloneObj[key] = deepClone(obj[key], hash)
     }
    }
    return cloneObj
}
let obj = { name: 1, address: { x: 100 } }
obj.o = obj; // 对象存在循环引用的情况 
let d = deepClone(obj); 
obj.address.x = 200; 
console.log(d);