深克隆与浅克隆的核心概念

  1. 基础前提:基本类型 vs 引用类型
    类型 存储位置 复制行为 示例
    基本类型 栈内存 复制值 (独立副本) NumberStringBooleanNullUndefinedSymbolBigInt
    引用类型 堆内存 复制引用 (指向同一堆内存) ObjectArrayFunctionRegExpDate
  2. 浅克隆 (Shallow Clone)
    1. 定义:仅复制对象的 「表层属性」 —— 基本类型属性复制值,引用类型属性复制引用 (新旧对象共享同一引用类型数据)
    2. 特点:修改新对象的引用类型属性,会同步影响原对象;
  3. 深克隆 (Deep Clone)
    1. 定义:递归复制对象的 所有层级属性 —— 无论是基本类型还是引用类型,都创建独立副本,新旧对象完全隔离;
    2. 特点:修改新对象的任意属性,都不会影响原对象;

浅克隆的多种实现方式

手动遍历赋值(通用版)

  1. 手动遍历对象 / 数组的表层属性,逐个复制值,适用于所有引用类型,可控性最强;

  2. 实现代码:

    function shallowClone(target) {
      // 排除非引用类型(基本类型直接返回自身)
      if (typeof target !== 'object' || target === null) return target;
    
      // 根据类型初始化克隆结果
      const cloneTarget = Array.isArray(target) ? [] : {};
      // 遍历表层属性(仅自身可枚举属性,不包含原型链)
      for (let key in target) {
        if (target.hasOwnProperty(key)) {
          cloneTarget[key] = target[key];
        }
      }
    
      return cloneTarget;
    }
    
    // 测试
    const obj = { a: 1, b: [1, 2], c: { d: 3 } };
    const cloneObj = shallowClone(obj);
    cloneObj.a = 2;
    cloneObj.b.push(3); // 原对象 obj.b 也会变成 [1,2,3]
    cloneObj.c.d = 4;   // 原对象 obj.c.d 也会变成 4
    
    console.log(obj);       // { a: 1, b: [ 1, 2, 3 ], c: { d: 4 } }
    console.log(cloneObj);  // { a: 2, b: [ 1, 2, 3 ], c: { d: 4 } }
    

Object.assign ()(对象专用)

  1. ES6 新增方法,将一个或多个源对象的自身可枚举属性复制到目标对象,返回目标对象;

  2. 注意:仅克隆表层,且无法克隆 Symbol 类型属性 (需手动处理)

  3. 实现代码:

    const obj = { a: 1, b: [1, 2], c: { d: 3 } };
    const cloneObj = Object.assign({}, obj);
    
    cloneObj.a = 2;
    cloneObj.b.push(3); // 原对象 obj.b 同步变化
    cloneObj.c.d = 4;   // 原对象 obj.c.d 同步变化
    
    console.log(obj);       // { a: 1, b: [ 1, 2, 3 ], c: { d: 4 } }
    console.log(cloneObj);  // { a: 2, b: [ 1, 2, 3 ], c: { d: 4 } }
    

数组专用浅克隆方法

  1. 数组的内置方法仅复制表层元素,属于浅克隆;

  2. 实现代码:

    const arr = [1, [2, 3], { a: 4 }];
    
    // 方式1:扩展运算符
    const cloneArr1 = [...arr];
    cloneArr1.push({ b: 5 });
    cloneArr1[1].push(4); 
    cloneArr1[2].a = 5;   
    console.log(arr);       // [ 1, [ 2, 3, 4 ], { a: 5 } ]
    console.log(cloneArr1); // [ 1, [ 2, 3, 4 ], { a: 5 }, { b: 5 } ]
    
    const arr = [1, [2, 3], { a: 4 }];
    
    // 方式2:Array.prototype.slice()
    const cloneArr2 = arr.slice();
    cloneArr2.push({ b: 5 });
    cloneArr2[1].push(4); 
    cloneArr2[2].a = 5;   
    console.log(arr);       // [ 1, [ 2, 3, 4 ], { a: 5 } ]
    console.log(cloneArr2); // [ 1, [ 2, 3, 4 ], { a: 5 }, { b: 5 } ]
    
    const arr = [1, [2, 3], { a: 4 }];
    
    // 方式3:Array.prototype.concat()
    const cloneArr3 = arr.concat();
    cloneArr3.push({ b: 5 });
    cloneArr3[1].push(4); 
    cloneArr3[2].a = 5;   
    console.log(arr);       // [ 1, [ 2, 3, 4 ], { a: 5 } ]
    console.log(cloneArr3); // [ 1, [ 2, 3, 4 ], { a: 5 }, { b: 5 } ]
    
    const arr = [1, [2, 3], { a: 4 }];
    
    // 方式4:Array.from()
    const cloneArr4 = Array.from(arr);
    cloneArr4.push({ b: 5 });
    cloneArr4[1].push(4); 
    cloneArr4[2].a = 5;   
    console.log(arr);       // [ 1, [ 2, 3, 4 ], { a: 5 } ]
    console.log(cloneArr4); // [ 1, [ 2, 3, 4 ], { a: 5 }, { b: 5 } ]
    

扩展运算符(对象 / 数组通用)

  1. ES6 扩展运算符 ... 可快速浅克隆对象 / 数组;

  2. 实现代码:

    const obj = { a: 1, b: [2, 3] };
    const cloneObj = { ...obj };
    
    cloneObj.a = 2;
    cloneObj.b.push(4);
    console.log(obj);       // { a: 1, b: [ 2, 3, 4 ] }
    console.log(cloneObj);  // { a: 2, b: [ 2, 3, 4 ] }
    
    const arr = [1, { x: 2 }];
    const cloneArr = [...arr];
    
    cloneArr[0] = 2;
    cloneArr[1].x = 3;
    console.log(arr);       // [ 1, { x: 3 } ]
    console.log(cloneArr);  // [ 2, { x: 3 } ]
    

Object.create ()(原型继承式克隆)

  1. 通过继承原对象的原型,克隆表层属性 (需手动复制属性)

  2. 实现代码:

    const obj = { a: 1, b: [2, 3] };
    const cloneObj = Object.create(Object.getPrototypeOf(obj)); // 继承原型
    Object.assign(cloneObj, obj); // 复制表层属性
    
    cloneObj.a = 2;
    cloneObj.b.push(4);
    console.log(obj);       // { a: 1, b: [ 2, 3, 4 ] }
    console.log(cloneObj);  // { a: 2, b: [ 2, 3, 4 ] }
    

深克隆的多种实现方式

JSON 序列化 / 反序列化

  1. 原理:将对象转为 JSON 字符串,再解析为新对象,自动递归复制;

  2. 局限性:

    1. 无法克隆 FunctionSymbolundefined 属性 (会丢失)
    2. 无法克隆循环引用的对象 (报错)
    3. 无法克隆 RegExpDate (转为字符串 / 数字)
    4. 无法克隆原型链上的属性;
    5. 数值精度可能丢失 (如 BigInt 转为数字)
  3. 实现代码:

    function deepCloneJSON(target) {
      return JSON.parse(JSON.stringify(target));
    }
    
    // 测试
    const obj = {
      a: 1,
      b: [2, 3],
      c: { d: 4 },
      e: () => { },     // 丢失
      f: Symbol('foo'), // 丢失
      g: undefined,     // 丢失
      h: new Date(),    // 转为字符串
      i: /abc/,         // 转为空对象 {}
    };
    console.log(deepCloneJSON(obj));
    /**
    {
      a: 1,
      b: [ 2, 3 ],
      c: { d: 4 },
      h: '2025-12-18T05:38:47.944Z',
      i: {}
    }
     */
    

递归手动克隆(基础版,处理核心类型)

  1. 手动递归遍历对象,区分类型复制,解决 JSON 方法的部分局限性;

  2. 实现代码:

    function deepCloneBasic(target) {
      // 基本类型直接返回
      if (typeof target !== 'object' || target === null) return target;
    
      // 处理 Date
      if (target instanceof Date) return new Date(target);
    
      // 处理 RegExp
      if (target instanceof RegExp) return new RegExp(target.source, target.flags);
    
      // 初始化克隆结果(保留数组/对象类型)
      const cloneTarget = Array.isArray(target) ? [] : {};
    
      // 遍历所有自身可枚举属性(包含 Symbol 类型)
      Reflect.ownKeys(target).forEach(key => {
        // 递归克隆属性值
        cloneTarget[key] = deepCloneBasic(target[key]);
      });
    
      return cloneTarget;
    }
    
    // 测试
    const obj = {
      a: 1,
      b: [2, { x: 3 }],
      c: new Date(),
      d: /abc/g,
      e: Symbol('foo'),
    };
    const cloneObj = deepCloneBasic(obj);
    cloneObj.b[1].x = 4;
    console.log(cloneObj);
    /**
    {
      a: 1,
      b: [ 2, { x: 4 } ],
      c: 2025-12-18T05:45:17.433Z,
      d: /abc/g,
      e: Symbol(foo)
    }
     */
    

递归克隆 + 循环引用处理(完整版)

  1. 解决 「循环引用」 问题 (如 obj.self = obj),通过 WeakMap 缓存已克隆的对象;

  2. 实现代码:

    function deepCloneComplete(target, cache = new WeakMap()) {
      // 1. 基本类型直接返回
      if (typeof target !== 'object' || target === null) return target;
    
      // 2. 处理循环引用:缓存已克隆的对象
      if (cache.has(target)) return cache.get(target);
    
      // 3. 处理特殊内置类型
      // 3.1 Date
      if (target instanceof Date) {
        const clone = new Date(target);
        cache.set(target, clone);
        return clone;
      }
    
      // 3.2 RegExp
      if (target instanceof RegExp) {
        const clone = new RegExp(target.source, target.flags);
        cache.set(target, clone);
        return clone;
      }
    
      // 3.3 ArrayBuffer
      if (target instanceof ArrayBuffer) {
        const clone = target.slice(0);
        cache.set(target, clone);
        return clone;
      }
    
      // 3.4 类型化数组(如 Uint8Array、Float64Array)
      if (ArrayBuffer.isView(target)) {
        const clone = new target.constructor(target.buffer.slice(0));
        cache.set(target, clone);
        return clone;
      }
    
      // 3.5 Set
      if (target instanceof Set) {
        const clone = new Set();
        cache.set(target, clone);
        target.forEach(item => clone.add(deepCloneComplete(item, cache)));
        return clone;
      }
    
      // 3.6 Map
      if (target instanceof Map) {
        const clone = new Map();
        cache.set(target, clone);
        target.forEach((value, key) => clone.set(key, deepCloneComplete(value, cache)));
        return clone;
      }
    
      // 4. 处理普通对象(保留原型链)
      const cloneTarget = Object.create(Object.getPrototypeOf(target));
      cache.set(target, cloneTarget); // 先缓存,避免递归循环
    
      // 5. 遍历所有自身属性(包含不可枚举、Symbol 类型)
      // 核心修正:Object.getOwnPropertyDescriptors 返回对象,需用 Object.entries 遍历
      const descriptors = Object.getOwnPropertyDescriptors(target);
      Object.entries(descriptors).forEach(([key, desc]) => {
        // 递归克隆属性值,并重写描述符(保留 writable/enumerable/configurable 等特性)
        desc.value = deepCloneComplete(target[key], cache);
        Object.defineProperty(cloneTarget, key, desc);
      });
    
      return cloneTarget;
    }
    
    // 测试用例
    // 1. 基础类型 + 引用类型
    const obj1 = {
      a: 1,
      b: [2, 3, { c: 4 }],
      d: new Date(),
      e: /abc/gi,
      f: Symbol('test'),
      g: new Set([1, 2]),
      h: new Map([['key', 'value']])
    };
    const clone1 = deepCloneComplete(obj1);
    console.log(clone1.b[2].c = 5); // 修改克隆对象,原对象不受影响
    console.log(obj1.b[2].c);       // 4(原对象未变)
    
    // 2. 循环引用
    const obj2 = { x: 10 };
    obj2.self = obj2; // 循环引用
    const clone2 = deepCloneComplete(obj2);
    console.log(clone2.self === clone2);  // true(克隆后仍保持循环引用)
    console.log(clone2.x);                // 10
    
    // 3. 不可枚举属性
    const obj3 = {};
    Object.defineProperty(obj3, 'hidden', {
      value: '不可枚举',
      enumerable: false
    });
    const clone3 = deepCloneComplete(obj3);
    console.log(Object.getOwnPropertyDescriptor(clone3, 'hidden').value); // 不可枚举
    console.log(Object.keys(clone3).includes('hidden'));                  // false(保留不可枚举特性)
    
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 深克隆与浅克隆的核心概念
  2. 2. 浅克隆的多种实现方式
    1. 2.1. 手动遍历赋值(通用版)
    2. 2.2. Object.assign ()(对象专用)
    3. 2.3. 数组专用浅克隆方法
    4. 2.4. 扩展运算符(对象 / 数组通用)
    5. 2.5. Object.create ()(原型继承式克隆)
  3. 3. 深克隆的多种实现方式
    1. 3.1. JSON 序列化 / 反序列化
    2. 3.2. 递归手动克隆(基础版,处理核心类型)
    3. 3.3. 递归克隆 + 循环引用处理(完整版)