📕 重学JavaScript: 需要注意哪些方法是浅拷贝,不要在代码中滥用?
嗨,大家好!这里是道长王jj
~ 🎩🧙♂️
今天来和大家聊聊关于浅拷贝的相关知识。
❓ 什么是浅拷贝
其实很简单,就是把一个东西复制一份,然后给另一个东西。😁
比如,你有一个数组叫 arr ,它里面有三个元素,就像这样:
let arr = [1, 2, 3];
然后,你想把这个数组给 newArr ,你可以这样写:
let newArr = arr;
这样,newArr 就和 arr 一样了。😊
但是,这样有一个问题,就是 newArr 和 arr 其实是同一个东西,只不过换了个名字而已。也就是说,它们指向的是同一块内存空间。😮
所以,如果你改变了 newArr 的内容,比如这样:
newArr[0] = 100;
那么,arr 的内容也会跟着改变,就像这样:
console.log(arr);//[100, 2, 3]
这时候,你可能想要的是 newArr 和 arr 是两个不同的东西,它们只是内容相同而已。也就是说,它们指向的是不同的内存空间。😁
这时候,你就需要用到拷贝了。拷贝就是把一个东西的内容复制一份,并放到另一个新的东西里。😊
比如,你可以用数组的 slice 方法来实现拷贝,就像这样:
let newArr = arr.slice();
这样,newArr 就和 arr 的内容一样了,但是它们不是同一个东西了。😊
所以,如果你改变了 newArr 的内容,比如这样:
newArr[0] = 100;
那么,arr 的内容不会跟着改变,就像这样:
console.log(arr);//[1, 2, 3]
这种拷贝叫做浅拷贝。浅拷贝只能复制对象的第一层属性,而不复制对象的深层属性。
也就是说,如果对象的属性是基本类型(数字、字符串、布尔值等),那么就直接复制这个值
如果对象的属性是引用类型(数组、对象、函数等),那么就复制这个引用(指针),而不复制引用指向的对象。😮
所以,浅拷贝有一个潜在的问题,就是如果对象里有嵌套的对象或数组,那么浅拷贝就无能为力了。比如这样:
let arr = [1, 2, {
val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr);//[ 1, 2, { val: 1000 } ]
咦!不是已经不是同一块空间的引用了吗?为什么改变了newArr改变了第二个元素的val值,arr也跟着变了。
这就是浅拷贝的限制所在了。它只能拷贝一层对象。如果有对象的嵌套,浅拷贝就无法实现完全的拷贝。😭
这一期我们先重点关注浅拷贝的实现方法。
❓ 我需要注意哪些方法是浅拷贝呢?
现在你有一个对象叫obj,它里面有几个属性,就像这样:
let obj = {
a: 1,
b: 2,
c: [3, 4]
};
第一种:手动遍历赋值
let newObj = {
};
for (let key in obj) {
// 判断属性是否是自身的
if (obj.hasOwnProperty(key)) {
// 把属性赋值给新对象
newObj[key] = obj[key];
}
}
第二种:用 Object.assign 方法
这个方法可以把一个或多个源对象的属性复制到目标对象上,并返回目标对象。
这个方法比较方便,但是要注意它只能复制可枚举的属性,而且如果源对象的属性是 getter 函数,那么它会执行这个函数,并把返回值赋给目标对象。
let newObj = Object.assign({
}, obj);
第三种:数组的 concat 方法
这个方法可以把一个或多个数组连接起来,并返回一个新的数组。
这个方法可以实现数组的浅拷贝,但是要注意它只能处理数组,而且如果数组里有引用类型的元素,那么它也只会复制引用。
let newArr = arr.concat();
第四种:数组的 slice 方法
这个方法可以返回一个数组的一部分,并返回一个新的数组。
这个方法也可以实现数组的浅拷贝,但是要注意它也只能处理数组,而且如果数组里有引用类型的元素,那么它也只会复制引用。
let newArr = arr.slice();
第五种:扩展运算符 …
这个运算符可以把一个可迭代对象展开成多个元素,并放在一个新的数组或对象里。
这个运算符可以实现数组或对象的浅拷贝,但是要注意它也只能复制可枚举的属性,而且如果源对象或数组里有引用类型的元素或属性,那么它也只会复制引用。
let newArr = [...arr];
let newObj = [...obj];
🎉 你觉得怎么样?这篇文章可以给你带来帮助吗?如果你有任何疑问或者想进一步讨论相关话题,请随时发表评论分享您的想法,让其他人从中受益。🚀✨