javascript——深浅拷贝

259 阅读1分钟

js的数据类型分为两大类:基本类型和引用类型。基本类型在栈中存放,但是引用类型的值在堆中存放,栈中存放的是堆地址。所以如果只对堆地址进行拷贝那么两个变量指向的是同一片堆区域。深拷贝就是把地址指向的内容也进行拷贝。

简而言之就是浅拷贝只进行一层关系的拷贝,深拷贝进行所有层次的拷贝。

手写浅拷贝

循环实现

function clone(obj){
    let newObj = {};
    for(let i in obj){
        newObj[i] = obj[i];
    }
    return newObj;
}

let people = {name: 'tom', age: 12};
let newPeople = clone(people);

扩展运算符实现

let people = {name: 'tom', age: 12};
let newPeople = {...people};

Object.assign(target,source)

let people = {name: 'tom', age: 12, arr:[1,2,3]};
let newPeople = Object.assign({},people);
people.arr.push(11);
console.log(newPeople.arr);//[1,2,3,11]

手写深拷贝

JSON.stringify和JSON.parse

把对象转化成字符串,然后通过parse解析成一个对象。

let people = {
    name: 'tom', 
    age: 12, 
    arr:[1,2,3],
    sex:{isMan:true},
    reg: /^asd$/,
    fun: function(){return this.name},
    syb: Symbol('foo')
};

let newPeople = JSON.parse(JSON.stringify(people));
console.log(people,newPeople);

可以看到这种深拷贝不能拷贝正则,Symbol,function。 而且原对象中如果两个引用相同,但是复制之后是不同的。

递归实现

juejin.cn/post/691912…

主要思路:

1.使用typeof区分要copy的target的类型,如果是基础类型(number,string,boolean,symbol,undefined)和函数(function) 就直接返回即可,就是一层浅拷贝。

2.使用instanceof 区分 object的类型。 如果是Date/RegExp就新实例化一个对象进行返回。

3.使用Object.getOwnPropertyDescriptors()获取对象的所有属性名,包括了enumerable,set,get等。

ES7的一个方法: Object.getOwnPropertyDescriptors方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。

4.Object.create()使用这个属性对象建立一个新对象,并且把原型指向源对象的原型。

5.源对象和新对象放到hashmap中,这样用于进行循环引用的赋值和相同引用的赋值。(克隆过就不用新建对象,直接找到之前克隆的对象进行返回)

6.使用Reflect.ownKeys()获取对象的所有属性(不可枚举和symbol属性)。然后进行递归。


function deepClone(target,hash = new WeakMap()){
    //要拷贝的是除了基础对象 和function时
    if(typeof target ==='object' && target !== null){

        //如果是Date就发你会一个新的实例
        if (target instanceof Date) return new Date(target);        
        //如果是正则也返回一个新的实例
        if (target instanceof RegExp) return new RegExp(target);


        // ------------利用weakMap处理循环引用,有就直接返回---------
        if (hash.has(target)) return hash.get(target);


         // ------------实现浅拷贝并且处理了原型链-----------------
        //获得所有属性的描述信息,可处理带有比如enumerable、set、get等描述信息的数据
        let allDesc = Object.getOwnPropertyDescriptors(target);
        //创建一个新对象,使用现有的对象来提供新创建的对象的proto
        let result = Object.create(target.__proto__, allDesc);
       

        hash.set(target, result);
        //-------------------Reflect.ownKeys 可获得不可遍历属性和Symbol属性-----------------
        for(let key of Reflect.ownKeys(target)){          
            result[key] = deepClone(target[key],hash)       
        }        
        return result;
    }else{
       //基础类型、function。
       return target;    
    }
}


let people = {
    name: 'tom', 
    age: 12, 
    arr:[1,2,3],
    reg: /^asd$/,
    fun: function(){return this.name},
    syb: Symbol('foo')

};

let sex = {isMan:true};
people.sex1 = sex;
people.sex2 = sex;
//sex1和sex2指向同一个引用。

let newPeople = deepClone(people);

people.sex1.isMan = false;
newPeople.sex1.isMan = false;