大白话JavaScript如何深拷贝一个对象或数组?JSON.parse (JSON.stringify ()) 这种方法有什么局限性?
如何深拷贝一个对象或数组
啥是深拷贝
深拷贝就像是给一个东西(对象或者数组)做了个完全一样的“克隆体”。这个“克隆体”和原来的东西没有任何关联,你对“克隆体”做任何修改,都不会影响到原来的东西,反过来也一样。
简单方法:JSON.parse(JSON.stringify())
这是一种比较简单的深拷贝方法。可以把它想象成,先把要拷贝的对象或者数组“翻译”成 JSON 格式的字符串,这个字符串就像是一个“蓝图”,然后再根据这个“蓝图”重新构建一个新的对象或者数组。
代码示例:
// 定义一个对象
const originalObj = {
name: '张三',
age: 25,
hobbies: ['篮球', '阅读']
};
// 使用 JSON.parse(JSON.stringify()) 进行深拷贝
const clonedObj = JSON.parse(JSON.stringify(originalObj));
// 修改克隆对象的属性
clonedObj.name = '李四';
clonedObj.hobbies.push('游泳');
console.log(originalObj);
// 输出: { name: '张三', age: 25, hobbies: [ '篮球', '阅读' ] }
console.log(clonedObj);
// 输出: { name: '李四', age: 25, hobbies: [ '篮球', '阅读', '游泳' ] }
从上面的代码可以看到,修改克隆对象的属性,原来的对象并没有受到影响。
手动实现深拷贝(递归方法)
手动实现深拷贝就是自己写代码来创建“克隆体”。对于对象和数组,需要递归地处理它们的每一个属性或者元素。
代码示例:
function deepClone(target) {
// 如果目标不是对象或者数组,直接返回
if (typeof target!== 'object' || target === null) {
return target;
}
let clone;
// 判断是数组还是对象
if (Array.isArray(target)) {
clone = [];
// 遍历数组元素
for (let i = 0; i < target.length; i++) {
// 递归调用深拷贝函数
clone[i] = deepClone(target[i]);
}
} else {
clone = {};
// 遍历对象属性
for (let key in target) {
if (target.hasOwnProperty(key)) {
// 递归调用深拷贝函数
clone[key] = deepClone(target[key]);
}
}
}
return clone;
}
// 定义一个对象
const original = {
name: '王五',
info: {
address: '北京'
}
};
// 使用自定义深拷贝函数
const cloned = deepClone(original);
// 修改克隆对象的属性
cloned.info.address = '上海';
console.log(original);
// 输出: { name: '王五', info: { address: '北京' } }
console.log(cloned);
// 输出: { name: '王五', info: { address: '上海' } }
这个自定义的 deepClone
函数会检查目标是对象还是数组,然后递归地处理每一个属性或者元素,确保“克隆体”和原来的东西完全独立。
JSON.parse(JSON.stringify())
方法的局限性
1. 无法处理函数和正则表达式
JSON.stringify()
会忽略对象中的函数和正则表达式,因为 JSON 格式不支持这些类型。这就好比在画“蓝图”的时候,把函数和正则表达式这些“特殊零件”给漏掉了。
代码示例:
const obj = {
name: '赵六',
sayHello: function() {
console.log('你好');
},
reg: /abc/
};
const clonedObj = JSON.parse(JSON.stringify(obj));
console.log(clonedObj);
// 输出: { name: '赵六' }
可以看到,sayHello
函数和 reg
正则表达式都没有被拷贝。
2. 无法处理 undefined
和 Symbol
类型
JSON.stringify()
会忽略对象中的 undefined
和 Symbol
类型的属性。这就像在“蓝图”里把这些类型的属性当成不存在一样。
代码示例:
const obj = {
name: '孙七',
age: undefined,
[Symbol('key')]: 'value'
};
const clonedObj = JSON.parse(JSON.stringify(obj));
console.log(clonedObj);
// 输出: { name: '孙七' }
age
属性和 Symbol
类型的属性都没有被拷贝。
3. 无法处理循环引用
循环引用就是对象的属性指向了对象本身或者形成了一个循环的引用关系。JSON.stringify()
遇到循环引用会报错,就像“蓝图”画到一半发现陷入了一个死循环,不知道该怎么画下去了。
代码示例:
const obj = {};
obj.self = obj;
try {
const clonedObj = JSON.parse(JSON.stringify(obj));
} catch (error) {
console.log(error);
// 输出: TypeError: Converting circular structure to JSON
}
这里 obj
的 self
属性指向了 obj
本身,JSON.stringify()
处理时就会报错。