Skip to content

[toc]

深浅拷贝的原理及手写

浅拷贝

1. 核心原理

浅拷贝是指:创建一个新对象,该对象具有原始对象属性值的精确副本

  • 基本类型:直接拷贝其值(如数字、字符串)
  • 引用类型:拷贝其内存地址。这意味着新旧对象指向同一个堆内存空间,修改其中一个嵌套对象,另一个也会受影响

2. 浅拷贝的多种实现方式

  1. Object.assign()

    • 特点:遍历源对象的可枚举属性,通过赋值方式处理

    • 注意:不拷贝继承属性和不可枚举属性,但支持 Symbol

      js
      let target = { a: 1 };
      let source = { b: { d: 2 } };
      Object.assign(target, source);
      
      source.b.d = 666; 
      console.log(target.b.d); // 666 (受影响)
  2. 扩展运算符 (...)

    在构造字面量对象时进行属性拷贝,语法最简洁

    js
    let obj1 = { a: 1, b: { c: 1 } };
    let obj2 = { ...obj1 };
  3. 数组特有:slice() 与 concat()

    • slice():返回选定元素的新数组

    • concat():合并数组并返回副本。 两者均不会改变原数组,但对内部的对象依然是浅拷贝引用

3. 手写实现浅拷贝

JavaScript
function shallowCopy(object) {
  if (!object || typeof object !== "object") return;
  let newObject = Array.isArray(object) ? [] : {};
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] = object[key];
    }
  }
  return newObject;
}

const original = {a: 1, b: {c: 2}};
const copied = shallowCopy(original);
console.log(copied); // {a: 1, b: {c: 2}}
console.log(original === copied); // false  
console.log(original.b === copied.b); // true

深拷贝

1. 核心原理

深拷贝会在堆内存中开辟一块全新的内存地址,递归地将原对象的所有层级属性全部拷贝过来。两个对象相互独立,修改任何一级属性都不会影响对方

2. 深拷贝的常见方案

  1. JSON 序列化 (最简便)

    JSON.parse(JSON.stringify(obj))

    • 优点:简单粗暴,解决 90% 的日常纯数据拷贝
    • 致命陷阱:无法拷贝函数、undefinedSymbol、正则对象、循环引用
  2. Lodash 库:_.cloneDeep

    在企业级项目中,推荐使用成熟的工具库,其内部处理了极其复杂的边界情况(如 Buffer、TypedArray 等)

3. 手写深拷贝

JavaScript
// 深拷贝
function deepCopy(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  const result = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      result[key] = deepCopy(obj[key]);
    }
  }
  return result;
}

const original = {a: 1, b: {c: 2}};
const cloned = deepCopy(original);
console.log(cloned); // { a: 1, b: { c: 2 } }  
console.log(original === cloned); // false  
console.log(original.b === cloned.b); // false

深浅拷贝对比

复制层级仅限第一层所有嵌套层级
内存表现引用类型共享堆内存地址引用类型拥有独立的堆空间
修改原对象嵌套属性修改会同步受影响互不干扰,完全独立
性能开销极低,速度快相对较高(取决于嵌套深度)
推荐用法简单状态合并(Object.assign)复杂业务对象克隆(Lodash)