堆与栈 ,深拷贝于浅拷贝

首先,先了解下 知识,js 的数据类型

堆和栈的区别

其实深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同。
堆和栈都是内存中划分出来用来存储的区域。

数据类型

  • 基本数据类型:String、Number、Boolean、Symbol、Undefined、Null
  • 基本类型:存在栈区间;
  • 引用数据类型:Object

基本数据类型存放在栈中:存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。
*基本数据类型值不可变:*基本数据类型的值是不可变的,动态修改了基本数据类型的值,它的原始值也是不会改变的
例如:

 var str = "abc";
    console.log(str[1]="f");    // f
    console.log(str);           // abc

引用类型存放在堆中 按照地址存储( 按址):

 var obj1 = {
  	name:"jack"
}

指针:地址,指向堆内存数据本身一个地址;

引用类型(object)是存放在堆内存中的,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况开进行特定的分配
在这里插入图片描述
引用数据类型值可变:

  var a = [1,2,3];
    a[1] = 5;
    console.log(a[1]); // 5

浅拷贝 VS深拷贝

传值和传址(通过基本数据和引用数据类型的却别之后,我们就应该能明白传值与传址的区别了)。
在我们进行赋值操作的时候,基本数据类型的赋值(=)是在内存中新开辟一段栈内存,然后再把再将值赋值到新的栈中

var a = 10;
var b = a;
a ++ ;
console.log(a); // 11
console.log(b); // 10

在这里插入图片描述

所以说,基本类型的赋值的两个变量是两个独立相互不影响的变量。

但是引用类型的赋值是传址例如,也就是说引用类型的赋值是对象保存在栈中的地址的赋值,这样的话两个变量就指向同一个对象,因此两者之间操作互相有影响

把地址复制过来,重新在栈区存储一个;

var a = {}; // a保存了一个空对象的实例
var b = a;  // a和b都指向了这个空对象

a.name = 'jozo';
console.log(a.name); // 'jozo'
console.log(b.name); // 'jozo'

b.age = 22;
console.log(b.age);// 22
console.log(a.age);// 22

console.log(a == b);// true

在这里插入图片描述

浅拷贝

看完上面的文章,其实上面的代码是我们常用的赋值引用,还不能算浅拷贝
那么

  • 赋值和浅拷贝有什么区别?
  var obj1 = {
        'name' : 'zhangsan',
        'age' :  '18',
        'language' : [1,[2,3],[4,5]],
    };

    var obj2 = obj1;

	obj2.name = "lisi";
    console.log(obj1);  

    //obj1 = {
    //    'name' : 'lisi',
    //    'age' :  '18',
    //    'language' : [1,["二","三"],["四","五"]],
    //}
    console.log(obj2);
    //obj2 = {
    //    'name' : 'lisi',
    //    'age' :  '18',
    //    'language' : [1,["二","三"],["四","五"]],
    //};


	  var obj1 = {
        'name' : 'zhangsan',
        'age' :  '18',
        'language' : [1,[2,3],[4,5]],
    };


    var obj3 = shallowCopy(obj1);

    function shallowCopy(src) {
        var dst = {};
        for (var prop in src) {
            if (src.hasOwnProperty(prop)) {
                dst[prop] = src[prop];
            }
        }
        return dst;
    }


    obj3.age = "20";
    obj3.language[2] = ["四","五"];
    console.log(obj3);
    //obj3 = {
    //    'name' : 'zhangsan',
    //    'age' :  '20',
    //    'language' : [1,["二","三"],["四","五"]],
    //};
  • obj1 原始数据
  • obj2 赋值数据
  • obj3 浅拷贝数据
    接下来说下浅拷贝。
    为什么改变了赋值得到的对象 obj2 和浅拷贝得到的 obj3 中的 language) 属性的第二个值和第三个值(language 是一个数组,也就是引用类型)。结果见输出,可以看出来,无论是修改赋值得到的对象 obj2 和浅拷贝得到的 obj3 都会改变原始数据。

【因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变。】

如果进行的是浅拷贝,拷贝的对象的属性如果是复杂类型,那么复杂类型部分的属性是相互影响

在深拷贝中,不管属性是什么数据类型,拷贝前后的对象互不影响;

用一句话简单理解 :

浅拷贝就是拷贝了一层,除了对象是拷贝的引用类型,其他(一开始讲到的基本数据类型)都是直接将值传递,有自己的内存空间的

深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象,
浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象

深拷贝

看完对浅拷贝的理解,可以知道:深拷贝就是对对象以及对象的所有子对象进行拷贝。
接下来改探讨的就是如何对对象进行深拷贝?

  • 1、用 JSON.stringify 把对象转成字符串,再用 JSON.parse 把字符串转成新的对象(使用JSON)。
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

坏处:它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。所以只适合 Number, String, Boolean, Array 的扁平对象

  • 2、递归拷贝
var obj1={

​    name:"喷火葫芦",

   obj2:{

​		name:"喷水葫芦",

​       love:["打妖精"]}
 }

口述: 把对象A进行循环遍历,如果这个的属性是基本类型,直接进行复制到一个空对象b
;如果这个属性的值是复杂类型,就递归,把这个复杂类型的数据进行再次循环;直接到这循环到所有的属性都是基本类型为止;

写:

function deepClone(obj) {
  let objClone = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === "object") {
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        //判断ojb子元素是否为对象,如果是,递归复制
        if (obj[key] && typeof obj[key] === "object") {
          objClone[key] = deepClone(obj[key]);
        } else {
          //如果不是,简单复制
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
} 

var obj1 = {
   a: 1,
   b: 2,
   c: {
     d: 3
   }
}
var obj2 = deepClone(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4

或者使用es6中的 Object.create()方法

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值