今天我们一起来探讨下js深拷贝和浅拷贝的理解
在深入拷贝之前我们先来了解下什么是数据类型,什么是堆,什么是栈。(数据类型 的理解可以参考我上一篇文章)
我们知道js数据类型分为基本类型和引用类型。
-
基本类型:String,Number,Boolean,Null,Undefined,Symbol,这些基本数据类型它们是直接按值存放的,所以可以直接访问。
-
引用类型:Function,Array,Object,当我们需要访问这些引用类型的值时,首先得从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
接下来,我们来看下堆和栈
栈(stack): 由操作系统自动分配内存空间,自动释放,存储的是基础变量以及一些对象的引用变量,占据固定大小的空间。
堆(heap):由操作系统动态分配的内存,大小不定也不会自动释放,一般由程序员分配释放,也可由垃圾回收机制回收。
理解了这些概念之后我们再来看什么是浅拷贝,什么是深拷贝
浅拷贝
浅拷贝:拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁的时候,指向这个内存空间的所有指针需要重新定义,不然会造成指针错误。
基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。 优点:相比于堆来说存取速度会快,并且栈内存中的数据是可以共享的。 缺点:相比于堆来说的缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
深拷贝
深拷贝:指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。
堆内存中的对象不会随方法的结束而销毁,就算方法结束了,这个对象也可能会被其他引用变量所引用(参数传递)。创建对象是为了反复利用(因为对象的创建成本通常较大),这个对象将被保存到运行时数据区(也就是堆内存)。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。
总结
通俗的来说:浅拷贝仅仅是复制了指向被复制的内存地址的指针,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。深拷贝是指在计算机中开辟了一块新的内存地址用于存放复制的对象。
如果需要了解深拷贝的实现方式,请看我的另一篇文章:深拷贝的实现