Object copying and comparison
The Difference Between Shallow Copy and Deep Copy
In JavaScript, object copying is divided into shallow copy and deep copy. A shallow copy only duplicates the first-level properties of an object, while a deep copy recursively copies all levels of the object.
// Shallow copy example
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
// Modifying the shallow-copied object affects the original
shallowCopy.b.c = 3;
console.log(original.b.c); // Outputs 3
There are multiple ways to implement deep copy:
// Using JSON methods for deep copy
const deepCopy = JSON.parse(JSON.stringify(original));
// Using recursion for deep copy
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;
}
Comparison of Common Copy Methods
JavaScript provides various object copying methods, each with its pros and cons:
-
Spread Operator (...)
const copy = { ...obj }; // Only performs shallow copy
-
Object.assign()
const copy = Object.assign({}, obj); // Also a shallow copy
-
JSON Methods
const copy = JSON.parse(JSON.stringify(obj)); // Deep copy but loses functions and Symbol properties
-
structuredClone()
const copy = structuredClone(obj); // Modern browser-supported deep copy API
The Complexity of Object Comparison
In JavaScript, objects cannot be compared simply using ==
or ===
operators:
const obj1 = { a: 1 };
const obj2 = { a: 1 };
console.log(obj1 === obj2); // false
console.log(obj1 == obj2); // false
This is because objects are compared by reference, not by content. To compare object contents, manual implementation is required:
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;
}
Handling Special Cases
In real-world development, object copying and comparison encounter special scenarios:
-
Circular References
const obj = { a: 1 }; obj.self = obj; // JSON.stringify will throw an error
-
Function and Symbol Properties
const obj = { func: () => console.log('hello'), [Symbol('id')]: 123 }; // JSON methods will lose these properties
-
Special Object Types
const date = new Date(); const copy = new Date(date.getTime()); // Date objects require special handling
Performance Considerations
Different copying methods vary significantly in performance:
// Performance test
const largeObj = { /* Object with many properties */ };
console.time('JSON method');
const copy1 = JSON.parse(JSON.stringify(largeObj));
console.timeEnd('JSON method');
console.time('Recursive implementation');
const copy2 = deepClone(largeObj);
console.timeEnd('Recursive implementation');
console.time('structuredClone');
const copy3 = structuredClone(largeObj);
console.timeEnd('structuredClone');
Generally, structuredClone()
performs best in modern browsers, while JSON methods may be faster for small objects but have limited functionality.
Practical Application Scenarios
-
State Management
// State updates in Redux reducers function reducer(state, action) { return { ...state, [action.type]: action.payload }; }
-
Immutable Data
// React state updates setUser(prev => ({ ...prev, profile: { ...prev.profile, age: 25 } }));
-
Cache Comparison
// Dependency comparison in React memo or useMemo const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Third-Party Library Solutions
Popular libraries offer more robust copying and comparison features:
-
Lodash
// Deep copy const copy = _.cloneDeep(obj); // Deep comparison _.isEqual(obj1, obj2);
-
Immer
import produce from 'immer'; const nextState = produce(currentState, draft => { draft.user.age += 1; });
-
Ramda
// Deep comparison R.equals(obj1, obj2);
Modern JavaScript Features
ES6+ introduced new features for object manipulation:
-
Object Spread
const merged = { ...obj1, ...obj2 };
-
Object.fromEntries
const copy = Object.fromEntries(Object.entries(obj));
-
Optional Chaining and Nullish Coalescing
const value = obj?.nested?.prop ?? 'default';
Type System Considerations
In TypeScript, copying and comparison require type safety:
interface User {
id: number;
name: string;
profile?: {
age: number;
};
}
function cloneUser(user: User): User {
return {
...user,
profile: user.profile ? { ...user.profile } : undefined
};
}
Browser Compatibility
Browser support varies for different copying methods:
structuredClone()
requires newer browser versions.- JSON methods are available in all modern browsers.
- Spread operator and
Object.assign()
require ES6 support.
// Compatibility check
if (typeof structuredClone !== 'function') {
// Fallback solution
window.structuredClone = obj => JSON.parse(JSON.stringify(obj));
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:继承实现方式
下一篇:JavaScript核心知识点