关于深浅拷贝的那些事

2020-08-08  本文已影响0人  阿羡吖

前言

所谓深浅拷贝,其实都是进行复制,主要区别在于复制出来的新对象和原来的对象时候会相互影响。
深浅拷贝的区分:B 复制 A,如何A 发生变化 B跟着变化 浅拷贝
反之, 如果 B 不发生变化 则为深拷贝。
浅拷贝 A B 指向同一个地址 所以 A受影响 B也会受影响

深浅拷贝实例

  var a =[1,2,3,4]
  var a_clone = a;
  console.log(a === a_clone);
  a_clone[0] = 5;
  console.log(a_clone);
  console.log(a);
image.png
  var obj=[1,2,3,[4,5]];
  var obj_extend =$.extend(true,{},obj);
  console.log(obj_extend === obj);
  console.log(obj_extend);
  console.log(obj);
image.png

要深入了解深浅拷贝必选先要了解其原理,这就不得不说到ECMAScript中的数据类型。

基本类型和引用类型

1、分类
基本类型:undefined,null,布尔值(Boolean),字符串(String),数值(Number)
引用类型:统称为Object类型,细分为:Function、Date、Array、Object类型等。
2、不同的数据存储形式
简单来说, 基本数据类型保存在栈内存 引用类型保存在堆内存
2.1.1 栈内存
栈内存中分别存储着变量的标识符以及变量的值

image.png
即:var a = "A"
在栈内存中是这样的
image.png
2.1.2 堆内存
栈内存存储的是变量的标识符以及对象在堆内存中的存储地址,但需要访问引用类型(对象、数组等)的值时,首先需要从栈中获得该对象的地址指针,然后再从对应的堆内存中取得所需的数据。
image.png
即:var a = {name:“jack”};
在内存中是这样的: image.png

3、不同类型的复制形式:
基本类型的复制:当你在复制时,相当于把值也一并复制给了新的变量

var a = 1;
var b = a;
console.log(a === b);
var a = 2;
console.log(a);
console.log(b);
image.png

改变a的值,并不会影响到b。

var a =1
image.png
var b = a;
image.png
a = 2;
image.png

引入类型:当复制引用类型时,实际上只是赋值起指向的内存地址,即原来的变量与复制的新变量指向了同一个东西

var a ={name:"jack",age:20};
var b = a;
console.log(a === b);
a.age =30;
console.log(a);
console.log(b)
image.png

可以看得出来:改变a的值,也会影响b的值
内存中是这样的

var a = {name:"jack",age:20}
image.png
var b = a;
image.png
a.age = 30;
image.png

看完上面之后,是不是就明白了。所谓深浅拷贝:
对于仅仅是复制了引用(地址),换言之,原来的变量和新的变量指向了同一个东西,彼此之间的操作会互相影响,为浅拷贝
反言之。如果是在堆中重新分配内存,拥有不同的地址,但是值是一样的,复制后的对象与原来的对象是完全隔离,互不影响的,为 深拷贝

深浅拷贝的主要区别:复制的是引用(地址)还是复制的是实例。

接下来就对上述实例进行深拷贝的实现

实现深拷贝方式

3.1、利用递归方式 对属性中所有的引用类型的值,遍历到是基本类型的值为止。
function deepCLone(obj){
    var targetObj = Array.isArray(obj)?[]:{};
    for(var keys in obj){
        if(obj.hasOwnProperty(keys)){
            if(obj[keys] && typeof obj[keys] === 'object'){
                targetObj[keys] = deepCLone(obj[keys])
            }else{
                targetObj[keys] = obj[keys]
            }
        }
    }
    return targetObj
}

var a ={name:"jack",age:20};
var b = deepCLone(a);
console.log(b ===a);
a.age = 30;
console.log(a);
console.log(b);
image.png
3.2、jQuery中的 extend复制方法

可以用来扩展对象,这个方法可以传入一个参数:deep(true or
false),表示是否执行深复制(如果执行深复制则会执行递归复制)

深拷贝

var obj = {name:"xixi",age:20,company:{name:"腾讯",address:"深圳"}};
var obj_extend =$.extend(true,{},obj);
console.log(obj === obj_extend);
obj.company.name="阿里";
obj.name="albabba";
console.log(obj);
console.log(obj_extend);
image.png

浅拷贝

var obj = {name:"xixi",age:20,company:{name:"腾讯",address:"深圳"}};
var obj_extend =$.extend(false,{},obj);
console.log(obj === obj_extend);
obj.company.name="阿里";
obj.name="albabba";
console.log(obj);
console.log(obj_extend);
image.png

从company的变化可以看出,貌似是浅拷贝,但是name又貌似是深拷贝,这是何解。

其实,Array的slice 和concat方法和jQuery中的extend复制方法,都是会复制第一层的值,对于第一层的值都是深拷贝,而到第二层的时候 Array的slice和concat方法就是复制引用,jQuery中的extend复制方法则取决于你的第一个参数,也就是时候进行递归复制。
所谓第一层 就是 key所对应的value值是基本数据类型,也就是像实例中的name,age,而对于value值是引用类型 则为第二层,也就是像上面的company。

顺便 我们可以试一下 slice 和concat

var a =[1,2,3];
var  b = a.slice();
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);
image.png

concat

var a =[1,2,3];
var  b = a.concat();
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);
image.png

看似是深拷贝,其实不然,下面的例子就说明了一切

var a =[[1,2,3],4,5];
var b= a.slice();
a[0][0] = 6;
console.log(a);
console.log(b);
image.png

这就很明显的看出来了,上面所述,第一层是深拷贝 第二层 是引用。

3.3、JSON对象的parse和stringify

JSON对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序化为一个js对象,这两个方法实现的是深拷贝

var obj ={name:'xixi',age:20,company:{name:'TX',address:'深圳'}};
var obj_json = JSON.parse(JSON.stringify(obj));
console.log(obj === obj_json);
obj.name="ali";
obj.company.name="AL";
console.log(obj);
console.log(obj_json);
image.png

妥妥的的深拷贝。

原文地址,膜拜大佬:https://zhuanlan.zhihu.com/p/26282765

上一篇下一篇

猜你喜欢

热点阅读