深浅拷贝
概念
js的数据类型分为一般数据类型(number、string、array、undefined和null)和引用类型(object),它们的存储形式不同,这里面就涉及到了堆和栈的概念

网上随便找了张图,具体可以看出,堆存放的是实体,栈中存放的是指针和一般数据类型的值;所以引用类型的实体改变会对拷贝的对象一起改变,这就引发了深浅拷贝的问题
浅拷贝 深拷贝
- 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,如果属性是基本类型那么拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象
- 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟新的一个区域存放新对象,且修改新对象不会影响原对象
针对引用类型来说 赋值 深拷贝 浅拷贝的区别
- 当我们把一个对象赋值给一个新的变量时,赋的其实是对象在栈中的地址,而不是堆中的数值,也就是两个对象其实指向的是同一个存储空间,无论哪个对象改变,其实改变的都是存储空间的内容,因此,两个对象是联动的
- 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但引用类型共享同一存储空间,会互相影响
- 深拷贝:从堆中重新开辟一个内存空间,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响
实现方式
-
深拷贝
1.递归拷贝
2.JSON.parse/JSON.stringify
缺点:new RegExp('\w+')转换会出现错误;会忽略function和值为undefined的字段
3..extend(bool,{timeout},param) 布尔值为true深拷贝
-
浅拷贝
1.object.assign()
2.lodash的_.clone
- ...展开运算符
4.concat
5.slice
实战演练
// 浅拷贝
function shallowCopy(obj) {
let target = {};
for (let i in obj) {
if(obj.hasOwnProperty(i)) {
target[i] = obj[i];
}
}
return target;
}
// 深拷贝
function deepClone(obj) {
let cloneObj = new obj.constructor(); //保持继承链
// let cloneObj = {}
if (obj === null) return obj; //跳出方法,防止堆栈溢出
if (obj instanceof Date) return new Date(obj); //处理日期问题
if (obj instanceof RegExp) return new RegExp(obj); //处理正则问题
if (typeof obj !== 'object') return obj;//跳出方法,防止堆栈溢出
for (let i in obj) {
if(obj.hasOwnProperty(i)) { //不遍历其原型链上的属性
cloneObj[i] = deepClone(obj[i]);
}
}
return cloneObj;
}
这里有三点需要注意:
1、用new obj.constructor ()构造函数新建一个空的对象,而不是使用{}或者[],这样可以保持原形链的继承;
2、用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性。
3.instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
语法:
object instanceof constructor
object:某个实例对象 constructor:某个构造函数
用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
网友评论