JS-深浅拷贝

87 阅读2分钟

JS中数据类型可以分为基本数据类型和引用数据类型

  • 基本数据类型:直接存储在栈(stack)中的数据

  • 引用数据类型:引用数据类型在栈中存储了指针,该指针指向堆中该实体的 起始地址 ,真实的数据存放在堆内存里。当解释器寻找引用值时,会首先检索其在栈中的 地址 ,取得地址后从堆中获得实体。

1 深拷贝和浅拷贝区别

浅拷贝 只复制指向某个对象的指针而不复制对象本身,新旧对象还是共享同一块内存。

但 深拷贝 会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

  • 当我们把一个对象赋值给一个新的变量时, 赋的其实是该对象的在栈中的地址,而不是堆中的数据 。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
  • 浅拷贝是按位拷贝对象, 它会创建一个新对象 ,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

2. 浅拷贝实现

(1) Object.assign()  

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

var obj = { a: {a: "kobe", b: 39} };

var initalObj = Object.assign({}, obj);

initalObj.a.a = "wade";

console.log(obj.a.a); //wade

注意: 当object只有一层的时候,是深拷贝 

let obj = {

    username: 'kobe'

};

let obj2 = Object.assign({},obj);

obj2.username = 'wade';

console.log(obj);//{username: "kobe"}

 (2) Array.prototype.concat()  

let arr = [1, 3, {

    username: 'kobe'

}];

let arr2=arr.concat();    

arr2[2].username = 'wade';

console.log(arr);// [1,3,{username: 'wade'}]

 (3) Array.prototype.slice()  

let arr = [1, 3, {

    username: ' kobe'

}];

let arr3 = arr.slice();

arr3[2].username = 'wade'

console.log(arr);// [1,3,{username: 'wade'}]

3 深拷贝实现

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

var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]

var new_arr = JSON.parse( JSON.stringify(arr) );

缺点:不能应用于函数类型、Symbol类型、undefined以及循环引用等属性的拷贝

(2) 手写代码实现

const isComplexType = obj => (typeof obj === 'object' || typeof obj === 'function')&&(obj !==null)
 
const deepClone = function (obj,hash = new Map()){
        if(obj.constructor === Date){
            return new Date(obj)
        }
        if(obj.constructor === RegExp) return new RegExp(obj)
 
        if(hash.has(obj)) return hash.get(obj)
        let allDesc = Object.getOwnPropertyDescriptors(obj)
 
        let cloneObj = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj), allDesc);
        hash.set(obj,cloneObj)
        for(let key of Reflect.ownKeys(obj)){
            cloneObj[key] = (isComplexType(obj[key]) && typeof obj[key] !=='function')?
            deepClone(obj[key],hash):obj[key]
        }
        return cloneObj
    }