JavaScript 深拷贝与浅拷贝完全指南:JSON、递归与结构化克隆

小飞兽 JavaScript 34 次阅读 2026-06-15

Introduction

JavaScript 中对象和数组是引用类型,直接赋值只是复制了引用地址,而非实际数据。理解浅拷贝与深拷贝的区别,以及如何正确实现深拷贝,是避免数据意外污染的前提。本文对比5种拷贝方案及其适用场景。

浅拷贝 vs 深拷贝

/* 浅拷贝:只复制第一层,深层仍是引用 */
var original = { a: 1, b: { c: 2 } };
var shallow = Object.assign({}, original);
shallow.b.c = 999;
console.log(original.b.c); // 999(被意外修改!)

/* 深拷贝:所有层级都复制,独立互不影响 */
var deep = JSON.parse(JSON.stringify(original));
deep.b.c = 999;
console.log(original.b.c); // 2(不受影响)*/

方法1:JSON.parse + JSON.stringify(简单场景)

var deep = JSON.parse(JSON.stringify(original));

/* 优点:语法简单 */
/* 缺点:

  • undefined、函数、Symbol 会被忽略

  • Date 对象变成字符串

  • RegExp、Error 对象变成空对象

  • NaN、Infinity 变成 null

  • 循环引用会报错

*/

方法2:structuredClone(现代方法,浏览器内置)

var deep = structuredClone(original);

/* 优点:

  • 支持 undefined、函数、Symbol

  • 支持循环引用(不报错)

  • 支持大部分内置类型

  • structuredClone({ date: new Date() }) 会保留 Date 对象

*/
/* 缺点:不能克隆函数(Functions cannot be structured-cloned)*/

方法3:手写递归深拷贝(完全可控)

function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    if (Array.isArray(obj)) {
        return obj.map(function(item) { return deepClone(item); });
    }
    var cloned = {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloned[key] = deepClone(obj[key]);
        }
    }
    return cloned;
}

方法4:lodash 的 cloneDeep

/* 如果项目有 lodash 依赖,可以使用 */
var deep = _.cloneDeep(original);
/* _.cloneDeep 处理了循环引用、函数、Symbol、正则等边界情况 */

常见问题

    • 循环引用:JSON.stringify 会报错,structuredClone 支持,lodash.cloneDeep 支持,手写递归需要额外处理(用 WeakMap 记录已拷贝的对象)。
    • 函数拷贝:JSON 和 structuredClone 都无法拷贝函数,只能手写递归保留函数引用。
    • Vue 中的深拷贝:Vue.set() 是响应式写入,不是拷贝。深拷贝后替换整个响应式对象才是正确的深拷贝方式。

    延伸阅读

  • lodash.cloneDeep 源码分析