浅拷贝和深拷贝

2019-07-11  本文已影响0人  isSunny

今天来讨论一下javascript中的浅拷贝和深拷贝。

首先我们先来看一下两个问题:

1.什么叫浅拷贝和深拷贝?

浅拷贝:将原对象的自身属性复制给新对象,如果原对象的属性是基本类型则直接进行值赋值,如果是引用类型则进行引用赋值,也就是说只进行一层赋值。

深拷贝:将原对象的自身属性复制给新对象,如果原对象的属性是基本类型则直接进行值赋值,如果是引用类型则复制这个引用类型,使得目标对象拥有一个引用类型且和这个源属性一模一样,而非是一个指针。

2.什么情况下浅拷贝、深拷贝?
由上面对两种拷贝的理解,很容易看出来,只有在对象里嵌套对象的情况下,才会根据需求讨论,我们要深拷贝还是浅拷贝。

接下来我们先了解一下js的基本类型和引用类型,有助于我们理解浅拷贝和深拷贝。

1.基本类型的变量和值是存在栈区的,如var a=1;var a = 2;如下图:


image.png

基本类型在赋值操作后,互相不影响。举个🌰

var a = 1;
var b = a;
a++;
console.log(a);//2
console.log(b);//1

2.引用数据类型:名存在栈内存,值存在堆内存,栈内存会提供一个指针指向堆内存中的值。如下图
var a = {};var b={}


image.png

所以赋值后,两个变量都保存在同一个对象地址,则两个变量指向同一个对象,所以改变任何一个变量都会受到影响。
举个🌰:

var a = {name:'li'};
var b = a;
console.log(a.name);//li
console.log(b.name);//li
a.name = 'zhang';
console.log(a.name);//zhang
console.log(b.name);//zhang
image.png

当a.name改变了,由于a与b指向的是同一个地址,所以自然b也受了影响。(另外引用赋值不是浅拷贝!! 引用赋值仅仅只是赋值个指针,两个变量都指向同一堆内存,而浅拷贝是使两个变量分别指向不同的堆内存)。


image.png
深拷贝和浅拷贝的区别:浅拷贝只是复制对象的第一层属性,而深拷贝可以对对象的属性进行递归复制。
浅拷贝:

1.for...in (只复制了对象的第一层,而对象内的对象是不同的引用,指向同一个内存空间,新的对象嵌套的对象改变,新的对象嵌套的对象也会改变)

var a = {name:"li",hobby:["eat","song","newthings"]};
        var b = {};
        for(var val in a){
            b[val] = a[val];
        }
        console.log(b);// {name:"li",hobby:["zhang","song","newthings"]};

       b.hobby[0] = "zhang";
       b.name = "chen";     
       console.log(a);  //{name:"li",hobby:["zhang","song","newthings"]};
       console.log(b);   //{name:"chen",hobby:["zhang","song","newthings"]};

2.数组 slice和concat
3.es6 扩展运算符...

深拷贝:

1.JSON.parse (JSON 并不支持函数类型的数据,所以对象中如果有函数类型不支持)

var obj= {c:1,d:[1,2,3,4,5]}
       var objNew = JSON.parse(JSON.stringify(obj));
       console.log(objNew);//{c:1,d:[1,2,3,4,5]}
       objNew.d.push(6);
       console.log(obj);//{c:1,d:[1,2,3,4,5]}
       console.log(objNew);//{c:1,d:[1,2,3,4,5,6]}

2.递归拷贝

 function deepClone(obj){
        //=>过滤特殊情况
        if(obj===null) return null;
        if(typeof obj !=='object') return obj;
        if (obj instanceof RegExp) return new RegExp(obj);
        if (obj instanceof Date) return new Date(obj);

        //判断是数组还是对象,不直接创建空对象目的:克隆的结果和之前保持相同的所属类
        let newObj = Array.isArray(obj)?[]:new obj.constructor;
        for(let key in obj){
            //如果是原型上的属性,不拷贝
            if(obj.hasOwnProperty(key)){
                newObj[key]=deepClone(obj[key]);
            }
        }
        return newObj;
        
    }
       //对象
       var obj= {c:1,d:[1,2,3,4,5]};
       var obj2 = deepClone(obj);
       console.log(obj2);////{c:1,d:[1,2,3,4,5]}
       obj.d.push(6);
       console.log(obj);//{c:1,d:[1,2,3,4,5,6]}
       console.log(obj2);//{c:1,d:[1,2,3,4,5]}
    
       var arr = [1,2,3,[1,2,4]];
        //数组
       var newArr = deepClone(arr);
       newArr[3].push(5);
       console.log(arr);//[1,2,3,[1,2,4]];
       console.log(newArr);//[1,2,3,[1,2,4,5]];

这里说一下Object.assign(),查阅了不同的资料,有把它归为浅拷贝的,也有把它归为深拷贝的,最开始也一脸懵,这里只能说如果拷贝的对象是非嵌套对象,可以说是深拷贝,如果对象中出现嵌套情况,那么它对被嵌套对象的行为就成了普通的浅拷贝了。也就是深拷贝一级对象,后面嵌套的属于浅拷贝,感觉和for..in一样。。。

3.jquery里的extend方法
$.extend( [deep ], {}, a, b )
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
{}:将合并结果保存到新对象,这样原对象将不会发生改变
a:第一个合并的对象
b:第二个合并的对象

哦了~~~

上一篇 下一篇

猜你喜欢

热点阅读