带你搞懂js的深拷贝
在JavaScript中,拷贝是一项非常重要的任务,因为在JavaScript中,赋值操作并不是简单的复制一个变量的值到另一个变量,而是复制该变量所持有的引用地址,这意味着如果你直接将一个变量赋值给另一个变量,那么两者将共享同一份数据,即数据的修改将会同步。因此,当你需要对数据进行操作和修改时,深拷贝是至关重要的。
深拷贝的实现
实现一个深拷贝的算法可以分为两个步骤:
- 将对象进行递归拷贝或者循环拷贝
- 将拷贝后的对象返回
递归拷贝
某些情况下,递归拷贝可以比循环拷贝更简单和可读性更好。一个基本的递归拷贝的实现如下:
function deepClone(obj) {
if (obj === null) return null; // 判断null
if (typeof obj !== 'object') return obj; // 判断基本类型,直接返回
if (obj.constructor === Date) return new Date(obj); // 判断Date类型
if (obj.constructor === RegExp) return new RegExp(obj); // 判断RegExp类型
const newObj = new obj.constructor(); // 通过构造函数创建对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]); //递归拷贝
}
}
return newObj; // 返回新对象
}
循环拷贝
循环拷贝是另一种实现深拷贝的方式,使用循环拷贝的好处是可以避免递归深度过深导致的栈溢出问题。一个基本的循环拷贝的实现如下:
function deepClone(obj) {
const stack = [];
const newObj = new obj.constructor();
stack.push({
parent: newObj,
key: undefined,
data: obj
});
while (stack.length !== 0) {
const node = stack.pop();
const { parent, key, data } = node;
if (typeof data === 'object') {
const clone = new data.constructor();
Object.defineProperty(parent, key, {
value: clone,
writable: true,
enumerable: false,
configurable: true
});
for (let i in data) {
stack.push({
parent: clone,
key: i,
data: data[i]
});
}
} else {
Object.defineProperty(parent, key, {
value: data,
writable: true,
enumerable: false,
configurable: true
});
}
}
return newObj;
}
在这个实现中,我们使用了一个栈(stack)来模拟递归过程。栈中每个元素代表了原始对象的一个属性,并且保存了当前拷贝对象上一级的父级、当前属性的键以及该属性所指向的原始对象。
示例说明
下面给短示两个简单的例子,帮助理解深拷贝过程。
示例 1:
const a = { name: 'Tom', birthday: new Date() };
const b = deepClone(a);
console.log(b); // { name: 'Tom', birthday: Date }
a.birthday.setFullYear(2000);
console.log(a.birthday); // 2000-xx-xx
console.log(b.birthday); // 当前日期
在这个例子中,我们定义了一个含有Date类型属性的对象,在将该对象深拷贝后,修改原始数据的Date类型属性,不会影响拷贝后的数据。
示例 2:
const a = { name: 'Tom', friends: ['Jerry', 'Bob'] };
const b = deepClone(a);
console.log(b); // { name: 'Tom', friends: ['Jerry', 'Bob'] }
a.friends.push('Kate');
console.log(a.friend); // ['Jerry', 'Bob', 'Kate']
console.log(b.friend); // ['Jerry', 'Bob']
在这个例子中,我们定义了一个含有数组属性的对象,在将该对象深拷贝后,修改原始数据的数组属性,不会影响拷贝后的数据。这是因为深拷贝过程中,数组被拷贝为一个新的数组,拥有自己的内存空间,不会受到原始数据的影响。
结语
关于JavaScript的深拷贝还有很多细节和实现方式,这里只提供了一个基本的模板和两个简单的示例。希望可以帮助你更好地理解深拷贝的概念和实现。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:带你搞懂js的深拷贝 - Python技术站