深拷贝与浅拷贝的原理,你知道吗?

751 阅读2分钟

1.定义

深拷贝:不受原数据影响的操作。

浅拷贝: 受原数据影响的操作。

2.代码解释

//深拷贝
let a = 1
let b = a
a = 2
console.log(b);//1 


//浅拷贝 
let a = { age:1 }
let b = a
a.age = 2
console.log(b) //{ age: 2 }

根据定义与代码的运行结果我们能清楚的知道:在第一段代码中,b的值赋给a之后改变a的值,b的值并不会改变。而在都二段代码中,b对象赋值给变量a之后,改变a对象里面的属性值,b对象里面的属性值发生了变化。这是经典的例题,为什么会这呢?

3.原理分析

function foo () {
    var a = '老许'
    var b = a
    var c = {name : '老王'}
    var d = c
}
foo()

在这样一段代码中,运行机制如下图所示:

8069A375F3931EF61E88AB9A56977DBD.jpg

首先内存是分为代码空间、栈空间和堆空间。而执行上下文是存放于栈空间里面,也就是图中左边区域。在foo的执行上下文中,有变量环境和词法环境(存放let和const变量),变量环境里面存放全局变量(var)和函数。

在所有变量中,所有的原始类型(string number boolean undefined null symbol bigint)的值都会放在本来的位置如:b='老许' a='老许',而那些引用类型(object Array function)的值则会放在堆空间里面,变量环境里面放入的是一个地址指针。而堆环境里面的地址是唯一的,所以c和d都指向同一个地址,也就是说,只要c d 任何一个发生了变化,都会影响另一个。

这就能解释深浅拷贝的原理了:值在栈里面就叫深拷贝,值在堆里面就叫浅拷贝

4.补充说明

所有的原始类型都放在调用栈里面,而所有的引用类型都放到堆里面。这是为什么呢?

其实因为栈的空间没有必要很大,因为原始类型所需空间本来就很小,函数每次调用完后执行上下文又会被释放,所以栈没有必要那么大空间。而引用类型就拿对象来说,里面可以放原始类型和引用类型,可以说是无穷大,所以引用类型所占空间很大,不应该放到栈里面。这样虽然是一个明智的选择,但是也有一个缺点:就是引用类型取值的时候要慢于原始类型的取值。因为每次取隐式类型的值时都需要查找堆里面的地址。多了一个步骤自然就慢了。