1.前言
深拷贝和浅拷贝是经常在面试中会出现的,主要考察你对基本类型和引用类型的理解深度。
说到深浅拷贝,那在这就不得不提一下内存和数据类型了。
在JS当中,数据类型分为基本数据类型和引用类型,其中基本数据类型(string,number,boolean,undefined,null,symnol......),引用类型为Object(Array、Object、Function......)。内存分为栈内存和堆内存,其中栈内存用来存储基本数据类型(存取速度快,存放量小)和引用类型的地址(存取速度慢,存放量大,其引用指针存于栈区,并指向引用本身),而堆内存则存储引用数据类型。
2.赋值
赋值是将某一数值或对象赋给某个变量的过程
2.1 基本数据类型:赋值,赋值之后两个变量互不影响
2.2 引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有影响
<script>
// 基本类型
var a = 100;
var b = a;
a = 200;
console.log(a, b); // 200, 100 ,a b指向不同的数据
// 引用类型指向同一份数据
var a = { c: 1000 };
var b = a;
a.c = 2000;
console.log(a.c, b.c); // 2000, 2000 全是2000,a b指向同一份数据
</script>
通常在开发中并不希望改变变量 a 之后会影响到变量 b,这时就需要用到浅拷贝和深拷贝。
3.浅拷贝
3.1 创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
<script>
function cloneShallow(source) {
var target = {};
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
return target;
}
var a1 = { b: { c: {} } };
var a2 = cloneShallow(a1); // 浅拷贝
a2.b.c = { d: "1" };
console.log("a1---", a1);
console.log("a2---", a2);
var a5 = { b: { d: [1,2] } };
var a6 = cloneShallow(a5); // 浅拷贝
a6.b.d = [3,4];
console.log("a5---", a5);
console.log("a6---", a6);
// 注意:当object只有一层的时候,是深拷贝,例如如下:
var a3 = { b:'9'};
var a4 = cloneShallow(a3);
a4.b = '10';
console.log("a3---", a3);
console.log("a4---", a4);
</script>
3.2 Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。注意,Object.assgin() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)
3.3 Array.prototype.concat()
3.4 Array.prototype.slice()
3.5 ...obj 展开运算符
展开运算符是 ES6 中新提出来的一种运算符。
4.深拷贝
4.1 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响
<script>
function isObject(obj) {
return typeof obj === "object" && obj != null;
}
function cloneDeep(source) {
if (!isObject(source)) return source; // 非对象返回自身
var target = Array.isArray(source) ? [] : {};
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep(source[key]); // 注意这里
} else {
target[key] = source[key];
}
}
}
return target;
}
var obj = {
title: "study",
list: ["1", "2", "3"],
};
var obj2 = cloneDeep(obj);
obj2.title = "play";
obj2.list = ["3", "4"];
console.log("obj", obj);
console.log("obj2", obj2);
</script>
4.2 JSON.parse(JSON.stringify(object))
JSON.stringify():将对象转成 JSON 字符串。
JSON.parse():将字符串解析成对象。
通过 JSON.parse(JSON.stringify()) 将 JavaScript 对象转序列化(转换成 JSON 字符串),再将其还原成 JavaScript 对象,一去一来我们就产生了一个新的对象,而且对象会开辟新的栈,从而实现深拷贝。
注意
注意,该方法的局限性:
1、不能存放函数或者 Undefined,否则会丢失函数或者 Undefined;
2、不要存放时间对象,否则会变成字符串形式;
3、不能存放 RegExp、Error 对象,否则会变成空对象;
4、不能存放 NaN、Infinity、-Infinity,否则会变成 null;
5、……更多请自行填坑,具体来说就是 JavaScript 和 JSON 存在差异,两者不兼容的就会出问题。
4.3 函数库 Lodash
cloneDeep() 方法
npm i --save lodash
var _ = require('lodash');
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
5.总结
通过学习整理了解到了深拷贝和浅拷贝的差异,在写业务代码的时候根据场景更好的拷贝数据,当然,文章也存在部分逻辑的不完整性,欢迎大家指正。
6 参考
木易杨前端进阶
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!