关于深拷贝
在做项目时遇到了表单填写后,不点击提交或确定,其数据也发生了变化;
这时候,我们需要使用到深拷贝去解决这个问题:
深拷贝
:创建一个新的对象和数组,将原对象的各项属性的"值"(数组的所有元素)拷贝过来,是"值"而不是"引用",新对象跟原对象不共享内存,修改新对象不会影响到原对象。
浅拷贝(Shallow Copy)
:将原对象的引用复制给新对象,新对象与原对象共享同一个内存地址,修改新对象会影响原对象,反之亦然。浅拷贝只复制了对象的引用,而没有复制对象本身。
解决方法一
将数据库获取数据赋值到一个tempIndex
中(非表单中的数据),当点击按钮修改非表单数据时深拷贝tempIndex
,赋值到index
,在表单中对index
做修改,点击确定或提交,把数据发送至后端,这样在没有点击确定或提交时原数据才不会跟着改变。
缺点
仅能用于防止意外修改,未实现深拷贝
解决方法二
function cloneJSON(source) {
return JSON.parse(JSON.stringify(source));
}
通过将对象转换为 JSON 字符串,然后再将 JSON 字符串解析为新对象的方式实现深拷贝,适用于大多数简单的对象和数组。
使用
JSON.stringify(source)
将源对象(source)序列化为 JSON 字符串。这个过程会将对象的所有属性和值转换为字符串表示。使用
JSON.parse()
将 JSON 字符串解析为新的对象。这个过程会创建一个新的对象,其中包含源对象的所有属性和值的拷贝。
缺点
- 无法处理循环引用的对象。如果对象中存在循环引用,
JSON.stringify()
会抛出错误。 - 无法拷贝函数、正则表达式、
undefined
、function
、symbol
等特殊对象。这些对象在序列化和解析过程中会丢失。 - 无法保留对象的原型链。新对象的原型链会被重置为
Object.prototype
。
解决方法三:递归实现
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
let clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
在循环中,函数使用
hasOwnProperty()
方法来检查属性是否为obj
自身的属性,以避免拷贝原型链上的属性。对于每个属性,函数递归调用
deepClone()
函数来拷贝属性的值,并将拷贝后的值赋给clone
对应的属性。
能够处理复杂的对象结构,包括嵌套的对象和数组。它会递归地遍历对象的所有属性,并对每个属性进行深拷贝。这样可以确保拷贝后的对象与原始对象完全独立,互不影响。
缺点
仍然无法处理循环引用的对象,因为递归调用会导致无限循环。
无法拷贝函数、正则表达式、
undefined
、function
、symbol
等特殊对象。
解决方法四:使用第三方库
lodash 是一个流行的 JavaScript 工具库,提供了许多实用的功能,包括深拷贝。您可以使用 lodash 的 cloneDeep
函数实现深拷贝:
const _ = require('lodash');
const newObj = _.cloneDeep(oldObj);
问题
Q1:如果我只需要对数组或对象一层进行拷贝,是不是可以用扩展运算符,为什么扩展运算符会拷贝对数组或对象的第一层,而不是所有层?
A:扩展运算符 ...
只会拷贝对象的第一层属性,而不会拷贝嵌套对象。这意味着,如果对象中包含嵌套对象,使用扩展运算符只会拷贝嵌套对象的引用,而不会拷贝嵌套对象本身。 例如:
const original = {
a: 1,
b: {
c: 2,
d: 3
}
};
const shallowCopy = { ...original };
shallowCopy.b.c = 42;
console.log(original.b.c); // 输出 42
shallowCopy.a=5
console.log(original.a); // 输出 1
在这个例子中,original
对象包含一个嵌套对象 b
。使用扩展运算符 ...
对 original
进行浅拷贝,得到 shallowCopy
。当修改 shallowCopy.b.c
的值时,original.b.c
的值也会发生变化,因为 shallowCopy.b
和 original.b
指向同一个对象。
缺点
- 使用扩展运算符可以实现一维的深拷贝,对于嵌套对象或者多层数组,扩展运算符只会拷贝第一层,内部的嵌套对象仍然是引用。
- 无法拷贝对象的不可枚举属性。使用扩展运算符只能拷贝对象的可枚举属性,不可枚举属性会被忽略。
- 无法拷贝对象的原型。使用扩展运算符只能拷贝对象自身的属性,原型链上的属性会被忽略。