JavaScript浅拷贝与深拷贝原理及实现详解

作者:代码拾荒时间:2026-10-26 13:21:51浏览:171

本文解析JavaScript中浅拷贝与深拷贝的核心区别。浅拷贝仅复制引用地址,修改嵌套对象会影响原数据;深拷贝则创建完全独立的新对象。文章通过Object.assign、展开运算符及JSON序列化等方法,结合递归实现,详细演示了两种拷贝模式的代码实现与注意事项。

前言

在JavaScript中,浅拷贝与深拷贝涉及对象(含数组等)复制时的行为差异:前者仅复制引用地址,后者则创建全新的独立对象。

浅拷贝仅处理基本类型数据,对于复杂类型仅复制其内存指针而不复制对象实体,导致新旧对象共享同一内存空间。若原始对象的属性或子对象发生变动,浅拷贝生成的对象也会随之改变。常见实现方式包括Object.assign()方法、展开运算符(...)以及手动遍历对象属性进行复制。

深拷贝会创建一个与原对象完全一致的新对象,两者不共享内存空间。修改深拷贝产生的新对象不会影响到原始对象。

什么是浅拷贝

执行JavaScript对象或数组的浅拷贝时,系统仅复制基本值的引用,而非完整内容。这意味着浅拷贝仅复制了原始对象属性或元素的引用,并未构建新的对象实例。

例如,假设存在一个简单的对象:

const obj = { name: 'John', age: 30 };

随后利用对象赋值语法创建浅拷贝:

const newObj = obj;

此时,newObj与obj指向相同的内存地址。若修改newObj的某一属性,obj的对应属性也会同步变更。

newObj.age = 40;
console.log(obj.age); // 输出 40

原因在于newObj与obj指向同一内存地址,因此对任一变量的修改都会反映在另一个变量上。

数组的浅拷贝同样遵循此逻辑:

const arr = [1, 2, 3];
const newArr = arr;
newArr.push(4);
console.log(arr); // 输出 [1, 2, 3, 4]

实现浅拷贝

浅拷贝可通过Object.assign()或展开运算符...来实现;

/**
 * 浅拷贝
 *
 * @param {Object} target 目标对象
 * @param {Object} source 源对象
 * @return {Object} 返回目标对象
 */
function extend(target, source) {
    for (var key in source) {
        if (source.hasOwnProperty(key)) {
            var value = source[key];
            if (typeof value !== 'undefined') {
                target[key] = value;
            }
        }
    }
    return target;
}

什么是深拷贝

与浅拷贝不同,深拷贝会复制整个对象或数组,包括所有嵌套的子对象和数组。新创建的对象与原始对象完全独立,修改其中一个不会影响另一个。

例如,利用JavaScript内置函数JSON.parse()和JSON.stringify()进行深拷贝:

const obj = { name: 'John', age: 30 };
const newObj = JSON.parse(JSON.stringify(obj));
newObj.age = 40;
console.log(obj.age); // 输出 30

该示例先通过JSON.stringify()将原始对象序列化为字符串,再利用JSON.parse()解析为新对象。由于字符串属于基本类型值而非引用类型,newObj成为一个全新对象,与obj无关联。

数组的深拷贝效果类似:

const arr = [1, [2, 3], [4, [5]]];
const newArr = JSON.parse(JSON.stringify(arr));
newArr[1][0] = 6;
console.log(arr[1][0]); // 输出 2

通过深拷贝创建新数组后,即使修改新数组中嵌套数组的元素,原始数组也不会受到影响。

实现深拷贝

深拷贝可采用JSON.parse(JSON.stringify(obj))、递归复制对象属性或使用lodash等第三方库的deepClone方法。需注意这些方法存在局限,如无法处理函数、循环引用等情况。

function deepCopy(obj) {
    var result, copy;
    if (obj.constructor === Object)
        result = {}; //判断传入的如果是对象,继续遍历
    else if (Array.isArray(obj))
        result = []; //判断传入的如果是数组,继续遍历
    else
        return obj; //如果是基本数据类型就直接返回
    for (var key in obj) {
        if(obj.hasOwnProperty(key)){
            copy = obj[key];
            if(copy===null){
                break;
            }
            if(typeof copy ==='undefined'){
                result[key]=undefined;
            }else if (getType(copy) == "Object"||getType(copy) == "Array")
                result[key] = deepCopy(copy); //递归方法
            else
                result[key] = copy; //基本数据类型则赋值给属性
        }
    }
    return result;
}
/**
* 深拷贝
* @param obj
* @param appeard
* @returns {*[]|any}
*/
deepCopy(obj, appeard = new Map()) {
    if (!(obj instanceof Object)) return obj; //如果是原始数据类型
    if (appeard.has(obj)) return appeard.get(obj); //如果已经出现过
    let result = Array.isArray(obj) ? [] : {};
    appeard.set(obj, result); //将新对象放入map
    //遍历所有属性进行递归拷贝
    [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)].forEach(key => (result[key] = this.deepCopy(obj[key], appeard)));
    return result;
}

至此,关于JavaScript浅拷贝与深拷贝原理及实现的解析结束。如需了解更多相关技术内容,可搜索本站往期文章或继续阅读后续章节,感谢支持!

作者最新文章