什么是浅拷贝?从基本类型和栈内存以及堆内存讲起.
我们在js中最常用的赋值方式就是浅拷贝.如: 两个对象a,b. 那么 让b等于a.
var b = a;
这就是最基本的浅拷贝了.那浅拷贝的表现形式是什么呢? 解决这个问题我们要先谈谈js中的数据类型了.
在es5中有6中数据类型它们是:undefined,null,Boolean,Stirng,Number,Object. 在es6中新增一个原始的数据类型Symbol那么js就有了7个数据类型了. 但是这些数据类型又可以分为基本数据类型和引用数据类型.-
基本数据类型(undefined,null,Boolean,Stirng,Number):
如何理解呢?它们保持的变量名(如:a)都标示在栈内存中的实际的值.如 var a = 1; 那么就是说a标示栈内存中的某一值为1的对象. -
引用数据类型(Object):
引用数据类型与基本数据类型不同点就是在"引用"这两个字上面.它本质上表示在栈内存上的一条指针记录.而指针所指的方向(指到堆内存里面),就是那块堆内存的对象,这才是引用类型真正的值.引用类型栈内存的表现其实就是一条指针记录.
那么究竟什么是浅拷贝呢?
浅拷贝就是栈内存的拷贝. 比如: 我把 a 对象的值赋给 b对象 就是把我a在栈内存里面的内容复制给b了.
var a = b
如果a是基本数据类型.那么b在栈内存里面获得的就是一个和a一样内容的东西(拷贝).他的内容是一个基本类型.
如果a是引用数据类型.那么b在栈内存里面获得的就是一个和a一样内容的东西(拷贝).它的内容是一个指针(指向同一块对内存).!!!(浅拷贝的本质!) 既然b获得是一条与a相同的指针记录.那么a和b本质上都连向同一块堆内存了! 这就是浅拷贝
所以当我们改变b对象中某个属性的值,a中的属性值也随之变化. 因为他们拥有相同的指针.
综上浅拷贝就是说两个对象拷贝的是栈内存中的内容; 基本类型就获得的是对应的值,而引用类型获得的就是指针!!!
指针是导致浅拷贝的表现的根本原因.
什么是浅拷贝的表现. 就是 b 变化了 a也随之变化. 且永远 a===b
代码如下:
let a = {name: 'ldj',age: 30}
let b = a b.age = 40 console.log(b) console.log(a) console.log(a === b)
什么是深拷贝?
通过上面的描述我们知道所谓浅拷贝就是栈内存里面的内容拷贝.而Object类型的话拷贝的就是指针记录(指向同一块对内存). 那深拷贝呢. 就是重新建一块堆内存了,b对象的指针指向这块新的堆内存.a和b指向了不同的堆内存(堆内存里面的属性的值如果是Object类型的话也指向新的堆内存).
深拷贝的方法:
- JSON.parse(JSON.stringify(obj)) 方法
let b = JSON.parse(JSON.stringify(a))
其实上面这一轮操作就是让b的指针从新获得一块堆内存了.
- 递归递归去复制所有层级属性
function deepClone(obj) { if (obj == null) return null; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (typeof obj !== 'object') return obj; let t = new obj.constructor for (let key in obj) { t[key] = deepClone(obj[key]) } return t;}复制代码
使用递归,每一个层级的去查.如果是引用类型.那就new一个新的对象(new 出来的东西肯定得有一块新的对内存来存储吧.) 然后再把整个新对象return出去.
[误区]还有其他深拷贝的方法了吗?
可能很多朋友认为.
let b = Object.assign({}, a)
或者
let b = {...a, ...{}}
如果a是 Array 类型的值
let b = [].concat(a)
或者
let b = [...a, ...[]]
以上几种方法其实指实现了第一层级的 '深'拷贝. 而没有实现真正意义上的深! 可以这样理解.它只调了deepCloneErr()方法一次 没有去做递归!
第一次写文章,文字差,没配图,请多包涵.
有错误请指正,
有疑问可共同探讨. 谢谢.