魯斯前端布魯斯前端

文章中英模式

布魯斯前端JS面試題目 - 深拷貝與淺拷貝深入解析

深入解析 JavaScript 中的深拷貝與淺拷貝機制,包含實作原理、效能考量、React 應用場景,以及面試常見問題的完整解答。

影片縮圖

懶得看文章?那就來看影片吧

基本概念

在 JavaScript 中,變數賦值時會根據資料型別採用不同的複製方式:

  • 基本型別:直接複製值(number、string、boolean、null、undefined、symbol)
  • 參考型別:複製參考位址(object、array、function)

淺拷貝 (Shallow Copy)

只複製第一層的值,巢狀物件仍然共享參考位址。常見方法:

// 1. Object.assign()
const original = { name: 'Bruce', info: { age: 25 } };
const copy1 = Object.assign({}, original);

// 2. 展開運算子
const copy2 = { ...original };

// 3. Array 方法
const arr = [1, [2, 3]];
const arrCopy1 = Array.from(arr);
const arrCopy2 = arr.slice();

深拷貝 (Deep Copy)

遞迴複製所有層級的值,創建完全獨立的新物件。常見方法:

// 1. structuredClone (推薦,但需注意瀏覽器支援)
const deepCopy1 = structuredClone(original);

// 2. JSON 方法 (有限制)
const deepCopy2 = JSON.parse(JSON.stringify(original));

// 3. 使用 Lodash 函式庫
// 需先安裝: npm install lodash
import _ from 'lodash';
const deepCopy3 = _.cloneDeep(original);

// 4. 遞迴實現
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  
  const copy = Array.isArray(obj) ? [] : {};
  
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      copy[key] = deepClone(obj[key]);
    }
  }
  
  return copy;
}

🔥 常見面試題目

(一)淺拷貝和深拷貝的區別?

解答:

const original = {
  name: 'Bruce',
  info: { age: 25 }
};

// 淺拷貝
const shallow = { ...original };
shallow.info.age = 26;
console.log(original.info.age); // 26 - 原物件被修改

// 深拷貝
const deep = structuredClone(original);
deep.info.age = 27;
console.log(original.info.age); // 26 - 原物件不受影響

(二)為什麼不建議使用 JSON.parse(JSON.stringify()) 進行深拷貝?

解答:JSON 方法有以下限制:

const obj = {
  func: () => console.log('Hello'),
  symbol: Symbol('test'),
  undefined: undefined,
  date: new Date(),
  regexp: /test/,
  [Symbol('key')]: 'value'
};

const copy = JSON.parse(JSON.stringify(obj));
console.log(copy.func);      // undefined - 函式丟失
console.log(copy.symbol);    // undefined - Symbol 丟失
console.log(copy.undefined); // undefined - undefined 丟失
console.log(copy.date);      // 字串 - 不再是 Date 物件
console.log(copy.regexp);    // {} - 正則表達式變成空物件
console.log(copy[Symbol('key')]); // undefined - Symbol key 丟失

(三)如何實現一個深拷貝函式?

解答:

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  
  const copy = Array.isArray(obj) ? [] : {};
  
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      copy[key] = deepClone(obj[key]);
    }
  }
  
  return copy;
}
function deepClone2(value, hash = new WeakMap()) {
  // Handle null and non-objects
  if (value === null || typeof value !== 'object') return value;
  
  // Handle Date objects
  if (value instanceof Date) return new Date(value);
  
  // Handle RegExp objects
  if (value instanceof RegExp) return new RegExp(value);
  
  // Handle circular references
  if (hash.has(value)) return hash.get(value);
  
  // Create new array or object
  const copy = Array.isArray(value) ? [] : {};
  
  // Record processed objects to avoid circular references
  hash.set(value, copy);
  
  // Recursively process all properties
  Object.entries(value).forEach(([key, val]) => {
    copy[key] = deepClone(val, hash);
  });
  
  return copy;
}

// Usage example
const obj = {
  array: [1, { a: 2 }],
  date: new Date(),
  regexp: /test/,
  func: function() {},
  nested: {
    deep: { value: 123 }
  }
};

// Create circular reference
obj.self = obj;

const cloned = deepClone(obj);
console.log(cloned.nested.deep.value); // 123
console.log(cloned.self === cloned); // true - maintains circular reference
console.log(cloned.self !== obj); // true - is a new object