Javascript 的赋值、引用以及深拷贝、浅拷贝
在之前总结 This 用法 的时候,遇到了传参问题,由此意识到了复制与引用的问题,说得高端些,引申出了深拷贝与浅拷贝的问题,后来在链家面试的时候,和一面也讨论到这个问题,当时的理解比较乱,涉及到深拷贝实现的方面都不熟悉,现在试着总结一下。
基本类型 & 引用类型
ECMAScript 中的变量类型分为两类
- 基本类型:number、string、boolean、null、undefined
- 引用类型:Object
两者的区别就是这篇文章要讨论的赋值问题,首先复习一下两种变量类型的存储方式。
基本类型
基本类型变量的存储方式如下图所示,栈内存中分别存储着变量的标识符以及变量的值。
所以 var a = 'leeon';
这里的 a 就是以下面的的方式进行存储。
引用类型
至于引用类型变量,参看下图,可以对比上面基本类型变量的存储结构。
引用类型变量与基本类型变量的区别是,栈内存存储的是变量的标识符以及对象在堆中的存储地址。可能听起来拗口,例如 var obj1 = {name: 'leeon'};
这样的对象定义之后,就会以下图的方式进行存储。
存储结构的差异只是前提,下面的代码才到重点
1 | // 情境一 |
这样的问题最初遇到时觉得很费解,但是从内存角度进行理解之后,答案开始清晰,情境一中,基本类型变量的拷贝,复制了变量的标识符以及值,a
与 b
的关系保持了独立。
而在情境二中,在引用类型变量这里,拷贝时复制的是变量的标识符与 对象存储地址,导致最后 obj1
和 obj2
都指向同一个对象,obj2
和 obj1
只是同一对象的两个面具,相互影响自然是必然的结果。
但是情境三中,对 obj2
的更改却没影响到 obj1
,这又是为什么?难道上面的理论有问题?当然不是,给 obj2
赋值一个新的对象,背后做的其实是将 obj2
的对象地址指向 {name: 'pine'}
,并不会影响到原对象,原理如下 :
这样的拷贝方式就引发了一个问题,我本想拷贝一个新的完全独立的新对象,最后只得出来一个壳,这是无法接受的(感受到了敷衍……)。这里就引出了深拷贝与浅拷贝之说(人们总喜欢造一堆高大上的概念)。
深拷贝 & 浅拷贝
由此可以明白,所谓浅拷贝,指的是仅拷贝对象地址,拷贝后的副本与原对象仍共用一块内存,而深拷贝则是将对象进行内存级别的复制,拷贝的副本与原对象保持独立。
如何实现深拷贝
JSON.parse()
1 | var oobj1 = {name: 'leeon',friens: {name:['john','kk','jd']}}; |
slice()
1 | var arr1=["1","2","3"]; |
concat()
1 | var arr1=["1","2","3"]; |
jQuery 的 extend()
参考:
- (美)David Flanagan. JavaScript权威指南(原书第6版) (Kindle 位置 3112-3125).
- 也来谈一谈js的浅复制和深复制
- JS是按值传递还是按引用传递?
- JS 进阶基本类型 引用类型 简单赋值 对象引用
作者: leeon
来源: https://leeon.im
链接: https://leeon.im/deep-clone-vs-shallow-clone/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可