对象拷贝与比较
浅拷贝与深拷贝的区别
JavaScript中对象拷贝分为浅拷贝和深拷贝两种方式。浅拷贝只复制对象的第一层属性,而深拷贝会递归复制对象的所有层级。
// 浅拷贝示例
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
// 修改浅拷贝后的对象会影响原对象
shallowCopy.b.c = 3;
console.log(original.b.c); // 输出3
深拷贝的实现方式有多种:
// 使用JSON方法实现深拷贝
const deepCopy = JSON.parse(JSON.stringify(original));
// 使用递归实现深拷贝
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
常见拷贝方法比较
JavaScript提供了多种对象拷贝方式,各有优缺点:
-
扩展运算符(...)
const copy = { ...obj }; // 只能浅拷贝
-
Object.assign()
const copy = Object.assign({}, obj); // 同样是浅拷贝
-
JSON方法
const copy = JSON.parse(JSON.stringify(obj)); // 深拷贝,但会丢失函数和Symbol属性
-
structuredClone()
const copy = structuredClone(obj); // 现代浏览器支持的深拷贝API
对象比较的复杂性
JavaScript中对象比较不能简单地使用==
或===
运算符:
const obj1 = { a: 1 };
const obj2 = { a: 1 };
console.log(obj1 === obj2); // false
console.log(obj1 == obj2); // false
这是因为对象比较的是引用地址而非内容。要实现对象内容的比较,需要手动实现:
function deepEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || obj1 === null ||
typeof obj2 !== 'object' || obj2 === null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
特殊情况的处理
在实际开发中,对象拷贝和比较会遇到一些特殊情况:
-
循环引用
const obj = { a: 1 }; obj.self = obj; // JSON.stringify会抛出错误
-
函数和Symbol属性
const obj = { func: () => console.log('hello'), [Symbol('id')]: 123 }; // JSON方法会丢失这些属性
-
特殊对象类型
const date = new Date(); const copy = new Date(date.getTime()); // Date对象需要特殊处理
性能考量
不同拷贝方法的性能差异很大:
// 性能测试
const largeObj = { /* 包含大量属性的对象 */ };
console.time('JSON方法');
const copy1 = JSON.parse(JSON.stringify(largeObj));
console.timeEnd('JSON方法');
console.time('递归实现');
const copy2 = deepClone(largeObj);
console.timeEnd('递归实现');
console.time('structuredClone');
const copy3 = structuredClone(largeObj);
console.timeEnd('structuredClone');
一般来说,structuredClone()
在现代浏览器中性能最好,而JSON方法在小对象上可能更快但功能有限。
实际应用场景
-
状态管理
// Redux reducer中的状态更新 function reducer(state, action) { return { ...state, [action.type]: action.payload }; }
-
不可变数据
// React状态更新 setUser(prev => ({ ...prev, profile: { ...prev.profile, age: 25 } }));
-
缓存比较
// React memo或useMemo的依赖比较 const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
第三方库解决方案
一些流行的库提供了更强大的拷贝和比较功能:
-
Lodash
// 深拷贝 const copy = _.cloneDeep(obj); // 深度比较 _.isEqual(obj1, obj2);
-
Immer
import produce from 'immer'; const nextState = produce(currentState, draft => { draft.user.age += 1; });
-
Ramda
// 深度比较 R.equals(obj1, obj2);
现代JavaScript特性
ES6+引入了一些有助于对象操作的新特性:
-
Object spread
const merged = { ...obj1, ...obj2 };
-
Object.fromEntries
const copy = Object.fromEntries(Object.entries(obj));
-
可选链和空值合并
const value = obj?.nested?.prop ?? 'default';
类型系统的考量
TypeScript中拷贝和比较需要考虑类型安全:
interface User {
id: number;
name: string;
profile?: {
age: number;
};
}
function cloneUser(user: User): User {
return {
...user,
profile: user.profile ? { ...user.profile } : undefined
};
}
浏览器兼容性
不同拷贝方法的浏览器支持情况:
structuredClone()
需要较新的浏览器版本- JSON方法在所有现代浏览器中都可用
- 扩展运算符和
Object.assign()
需要ES6支持
// 兼容性检查
if (typeof structuredClone !== 'function') {
// 回退方案
window.structuredClone = obj => JSON.parse(JSON.stringify(obj));
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:继承实现方式
下一篇:JavaScript核心知识点