JavaScript中浅拷贝和深拷贝,栈堆详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40983119/article/details/82995151

简单来说,JavaScript中单个等于号的赋值就是拷贝,但是呢这个这个赋值可以分为两种,一种是赋的是数据,一种是赋内存地址,在看深浅拷贝前,先来看下JavaScript的数据类型,为甚么看JavaScript的数据类型呢

因为深浅拷贝与数据类型有很大很大的关系,这能帮助你更好的理解深浅拷贝这个知识点。

ECMAStript变量包含两种不同类型的值,基本类型和引用类型。

  • 基本类型:指的就是保存在栈内存中的简单数据段。
  • 引用类型:指的是那些保存在堆内存中的对象,换句话说,就是变量名实际上是一个指针,而这个指针指向的位置,就是保存对象的位置。

两种数据类型对应两种不同的访问方式

  • 基本类型:按值访问,操作的是它们实际的值,基本的数据类型有:undefined,boolean,number,string,null.
  • 引用类型:按引用访问,当查询时,我们需要先从栈中读取内存地址,然后按照指针所指向的地方,找到堆内存里面的值。

引用类型:javascript中除了上面的基本类型(number,string,boolean,null,undefined)之外就是引用类型了,也可以说是就是对象了。主要有:

  • Object是一个基础类型,其他所有类型都是从Object继承基本的行为;
  • Array类型是一组值的有序列表,同事还提供了操作和转换这些值的功能;
  • Date类型提供有关日期和时间信息,包括当前日期和时间已经相关的计算功能;
  • RegExp类型是支持正则表达式的。
  • function,函数实际上是Function类型的实例,因此函数也是对象,函数也拥有方法,可以来增强其行为。

耐心看下去哦,这并没有跑偏话题哦!

说到这里很有必要说下栈和堆内存了,什么是栈和堆?在计算机领域中,堆栈是两种数据结构,它们只能在一端(称为栈顶(top))对数据项进行插入和删除;可以理解为两种数据结构。

  • 栈:先进后出;动态分配的空间 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
  • 堆:队列优先,先进先出;由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

JavaScript中的基本类型和引用类型与堆栈有什么联系?

  •  基本类型:Undefined、Null、Boolean、Number 和 String,这5中基本数据类型,是按照值进行分配的,存放在栈(stack)内存中的简单数据段,数据大小确定,内存空间大小可以分配。 值得注意的是存在栈内存中的数据,一般都是大小已经确定的数据(不同于数组或对象这两者,比如数组的长度一般是可以动态变化动态增删改的,对象也同理);
  • 引用类型:即存放在堆(heap)内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。

回到正题:

  • 深拷贝:其实就是基本数据类型在栈内存中的赋值,这个赋值是什么意思呢?在栈内存中开辟一个内存空间,里面放着着你的基本数据类型,具体点就是增加一个指针(指针你可以理解为内存索引)并且申请一个新的内存,使这个增加的指针(内存索引)指向这个新的内存,然后你就可以根据指针(内存索引)找到你要的数据了(这也是获取栈内存中数据的原理)。
  • 浅拷贝:其实就是引用数据类型在堆中的引用(什么叫引用,前面说了就是内存地址),说白了就是把这个内存地址复制一份给到另外一个变量。
  • 综上所得深拷贝是相对基本数据类型来说的,浅拷贝相对引用数据类型来说的,深拷贝拷的是数据,浅拷贝拷的是指针;

好了理论有了,开始上代码: 

var obj = {
    name:'张三',
    age:20
};//此时obj是属于引用类型Object,在堆中开辟一个内存地址,里面放着name和age两
//个数据,obj为指向堆中这些数据的指针(也就是索引)


//这是浅拷贝,这行代码执行的意思是把obj这个指针赋值给b,那么b也是指针了,也指向堆中的name和age这个数据
var b = obj;

//深拷贝,这行代码就是把age这个值赋给c,为啥说是赋值呢,因为age为基本类型他的值是确定的,所以这个
//意思在栈中开辟一个内存,里面放着20这个数据,而c为指向这个20的指针(c理解为内存地址或者索引都可以)
var c = obj.age;

console.log(b.name); //张三
console.log(c);      //20
//改变b的name属性的值,那也等于改变了obj的name属性的值,为啥这样呢?因为前面说过了b和obj都是指针
//他们指向的数据name和age都是一样的,所以通过b.name来改变name的值和通过obj.name来改变name的值
//其实都是一样的,所以改了b.name也等于改了obj.name
b.name = 'akuan';
c = 22;//基本数据类型相互独立,所以改了c不会影响到b和obj里面的age
console.log(obj1.name);     //akuan
console.log(obj1.age);       //22

那么怎么拷贝引用类型,又使得他们之间相互独立不受影响呢?上代码

var obj1 = {
        name: "张三",
        age: "30"
};

function deepCopy(source) {
   var result = {};
   for (var key in source) {
       result[key] = typeof source[key] === 'object' ? deepCopy(source[key]) : source[key];
   }
   return result;
}
    //把obj1拷贝给obj2
    var obj2 = deepCopy(obj1);
    
    //这时候改变obj2的name值看下obj1的name会不会变
    //不会变就说明他们之间相互独立不受影响。
    obj2.name="李四";
    console.log(obj1.name);//张三

 写到这里,深浅拷贝和数据类型基本写完了,记得动手动脑哦,这会更加有利于你理解的

猜你喜欢

转载自blog.csdn.net/weixin_40983119/article/details/83001690
今日推荐