浅谈深浅拷贝
最近和人聊起到深浅拷贝的问题,感触有点,觉得这个问题可深可浅,面试经常会遇到深浅拷贝的问题,主要考察你对数据类型的掌握理解,不过很多人对这块也说的不是很清晰,今天就简单来谈谈我的个人看法
说到深浅拷贝就必须要提到数据类型的内存存储方式
在js引擎中对数据的存储主要有两种位置,栈内存和堆内存。
数据分为:
- 基本数据类型(String, Number, Boolean, Null, Undefined,Symbol(new in ES 6)
- 引用数据类型(统称为 Object 类型,细分的话有:Object 、Array 、Date 、RegExp、Function… )。
特点:
1、 基本数据类型的特点:直接存储在栈(stack)中的数据
2、 引用数据类型的特点:将该对象引用地址存储在栈中,然后对象里面的数据存放在堆中。
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
切入主题,在我看来,深浅拷贝就是传值和传址的问题,它主要针对的是引用类型数据
浅拷贝:只拷贝某个对象的指针而不拷贝对象本身,即他们的指针指向同一内存空间,也就是说,只要你更改了拷贝对象的数据,那内存空间里的数据即被更改,原来的数据也会跟着被更改。
// 定义变量
let aObj = {
uname: 'z3',
age: 18
}
// 浅拷贝
let bObj = aObj
// 输出 -> 修改 -> 输出
console.log(aObj.uname) //z3
console.log(bObj.uname) //z3
bObj.uname = 'l4'
console.log(aObj.uname) //l4
console.log(bObj.uname) //l4
深拷贝:拷贝某个对象,并且在内存中开辟一个新的内存空间,指针指向新的内存空间,即修改它的值,不影响原来对象的数据。
// 定义变量
let a = 666
// 浅拷贝
let b = a
// 输出 -> 修改 -> 输出
console.log(a) //666
console.log(b) //666
b = '777'
console.log(a) //666
console.log(b) //777
总结优缺点:
浅拷贝:不会造成内存的浪费,但是在某些复杂灵活的环境中,例如一个对象在多出被用到,在某一处不小心修改了其数据,其他地方就很难预见数据是如何改变的,这时候需要使用深拷贝来解决问题
深拷贝:解决了浅拷贝的问题,但是又造成了内存的浪费
如何将浅拷贝转为深拷贝,有几种方法
1、展开运算符(有瑕疵,只针对简单数据有效,复杂多维无效)
2、Object.assign() 有瑕疵,同上
3、JSON的解析方式
JSON.parse(JSON.stringify(obj))
4、递归方法
function deepCopy(obj) {
var result = Array.isArray(obj) ? [] : {};
for (var key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null) {
result[key] = deepCopy(obj[key]) //递归复制
} else {
result[key] = obj[key]
}
}
return result
}
let b = deepCopy(a)
5、lodash模块 (详情https://www.npmjs.com/package/lodash)
6、immutable模块 (详情https://www.npmjs.com/package/immutable)