文章来源稀土掘金技术社区——勇宝趣学前端
一、情景再现
二、JavaScript数据类型
金三银四
,最近找工作的小伙伴是扑面而来,这其中少不了我的好朋友张某某同学,我们‘相依为命’
,我经常开导他,这不最近的一次面试中他就遇到这样一个考题:面试官:(...)是深拷贝吗?
在聊深浅拷贝之前,我们先来说一说JS
中的数据类型:
我们都知道JavaScript中有两种数据类型(基本类型
和引用类型
),那么我就先考考大家:数据类型都有什么?
-
基本数据类型:String(字符串)、Number(数值)、Boolean(布尔值)、Null、Undefined、Symbol;
- 基本数据类型是直接存储在栈中。
-
引用数据类型:Array、Object;
- 引用类型存储的是该对象在栈中的引用,真正的数据是存储在内存(堆)中的。
举例一
let name = 'iyongbao';
let name2 = name;
name2 = 'zhangsan';
console.log(name); // iyongbao
console.log(name2); // zhangsan
举例二
let obj = { name: 'iyongbao' };
let obj2 = obj;
obj2.name = 'zhangsan';
console.log(obj); // {name: "zhangsan"}
console.log(obj2); // {name: "zhangsan"}
上线的案例都是‘拷贝’
,从中我们可以看到,引用类型的赋值会影响到原数据,这其实就是浅拷贝
。
对于深浅拷贝,勇宝给出自己的理解:
浅拷贝:对于浅拷贝也就是基本数据类型,拷贝后的值无论怎么变化都不会影响到原数据;而对于引用类型来说,有的人认为浅拷贝是会拷贝对象的第一层值,也就是说对象通过浅拷贝当我们修改新复制对象的一层属性时,原数据不会发生改变。
深拷贝:无限层级拷贝。在深拷贝中,修改基本数据类型和引用数据类型都不会影响原有的数据类型。
// 浅拷贝
let obj = { a: 1, b: { c: 2 } };
let obj2 = { ...obj };
obj2.a = 3;
obj2.b.c = 4;
console.log(obj); // {a:1, b: { c: 4 }}
console.log(obj2); // {a: 3, b: { c: 4 }}
结论:通过上边的案例,我们可以看出...
(拓展运算符)是一个浅拷贝
。
// 深拷贝
let obj = { a: 1, b: { c: 2 } };
let obj2 = JSON.parse(JSON.stringify(obj));
obj2.a = 3;
obj2.b.c = 4;
console.log(obj); // {a:1, b: { c: 2 }}
console.log(obj2); // {a: 3, b: { c: 4 }}
关于JSON.stringify
大家可以看一看我的另一篇文章:你不知道的JSON.stringify神操[1]
3.1 直接赋值
let obj = {
name: 'iyongbao',
age: 26
}
let obj2 = obj;
obj2.name = "zhangsan";
console.log(obj); // {name: "zhangsan", age: 26}
console.log(obj2); // {name: "zhangsan", age: 26}
3.2 Object.assign
let obj = {
name: 'iyongbao',
score: {
vue: 98
}
}
let obj2 = Object.assign({}, obj);
obj2.name = "zhangsan";
obj2.score.vue = 60;
console.log(obj); // {name: "iyongbao", score: { vue: 60 }}
console.log(obj2); // {name: "zhangsan", score: { vue: 60 }}
注意:使用Object.assign
的第一层
是深拷贝。
3.3 扩展运算符
let obj = {
name: 'iyongbao',
score: {
vue: 98
}
}
let obj2 = { ...obj };
obj2.name = "zhangsan";
obj2.score.vue = 60;
console.log(obj); // {name: "iyongbao", score: { vue: 60 }}
console.log(obj2); // {name: "zhangsan", score: { vue: 60 }}
注意:扩展运算符
和Object.assign
的效果一样。
3.4 slice和concat
slice是截取数组,concat是拼接数组。
let obj = ['iyongbao', { vue: 98 }]
let obj2 = obj.slice();
let obj3 = obj.concat();
obj[0] = "zhangsan";
obj[1].vue = 60;
console.log(obj); // {name: "zhangsan", score: { vue: 60 }}
console.log(obj2); // {name: "iyongbao", score: { vue: 60 }}
console.log(obj3); // {name: "iyongbao", score: { vue: 60 }}
四、深拷贝方法
4.1 JSON.parse(JSON.stringify(待拷贝对象))
这里使用的还挺多的。
let obj = {
name: 'iyongbao',
age: 26
}
let obj2 = JSON.parse(JSON.stringify(obj));
obj2.name = "zhangsan";
console.log(obj); // {name: "iyongbao", age: 26}
console.log(obj2); // {name: "zhangsan", age: 26}
4.2 使用第三方库 Lodash
const _ = require('lodash');
let obj = {
name: 'iyongbao',
age: 26
}
let obj2 = _.cloneDeep(obj);
obj2.name = "zhangsan";
console.log(obj); // {name: "iyongbao", age: 26}
console.log(obj2); // {name: "zhangsan", age: 26}
4.3、手写一个深拷贝
JSON.stringify还是存在一些不足的,比如对`(函数、undefined、正则、Symbol)不友好,下面我们就自己来动手写一个简单的深拷贝方法。
function deepClone (obj) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
let deepCloneObj = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
deepCloneObj[key] = deepClone(obj[key]);
}
}
return deepCloneObj;
}
五、总结
深浅拷贝
在JavaScript
开发中有着不同的应用场景和实现方式。了解它们的区别对于正确处理对象和数组的赋值是至关重要的。希望通过今天的分享,能够帮助小伙伴们更好的去加深与理解