阿里云主机折上折
  • 微信号
Current Site:Index > Object copying and comparison

Object copying and comparison

Author:Chuan Chen 阅读数:45363人阅读 分类: JavaScript

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:

  1. Spread Operator (...)

    const copy = { ...obj };
    // Only performs shallow copy
    
  2. Object.assign()

    const copy = Object.assign({}, obj);
    // Also a shallow copy
    
  3. JSON Methods

    const copy = JSON.parse(JSON.stringify(obj));
    // Deep copy but loses functions and Symbol properties
    
  4. 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:

  1. Circular References

    const obj = { a: 1 };
    obj.self = obj;
    // JSON.stringify will throw an error
    
  2. Function and Symbol Properties

    const obj = { 
      func: () => console.log('hello'),
      [Symbol('id')]: 123
    };
    // JSON methods will lose these properties
    
  3. 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

  1. State Management

    // State updates in Redux reducers
    function reducer(state, action) {
      return {
        ...state,
        [action.type]: action.payload
      };
    }
    
  2. Immutable Data

    // React state updates
    setUser(prev => ({
      ...prev,
      profile: {
        ...prev.profile,
        age: 25
      }
    }));
    
  3. 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:

  1. Lodash

    // Deep copy
    const copy = _.cloneDeep(obj);
    
    // Deep comparison
    _.isEqual(obj1, obj2);
    
  2. Immer

    import produce from 'immer';
    
    const nextState = produce(currentState, draft => {
      draft.user.age += 1;
    });
    
  3. Ramda

    // Deep comparison
    R.equals(obj1, obj2);
    

Modern JavaScript Features

ES6+ introduced new features for object manipulation:

  1. Object Spread

    const merged = { ...obj1, ...obj2 };
    
  2. Object.fromEntries

    const copy = Object.fromEntries(Object.entries(obj));
    
  3. 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:

  1. structuredClone() requires newer browser versions.
  2. JSON methods are available in all modern browsers.
  3. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.