You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript中对象引用赋值为何修改原对象?如何避免?

理解JavaScript对象引用与避免修改原对象的方法

嘿,这个问题其实戳中了JavaScript里引用类型的核心特性,咱们一步步拆解清楚~

首先先看你的代码示例(已经格式化好啦):

const config = {a: {x: 1, y: 2, z: 3}, b: {x: 12, y: 23, z: 34}};
console.log(config); 
// ->
// [object Object] {
//   a: [object Object] {
//     x: 1,
//     y: 2,
//     z: 3
//   },
//   b: [object Object] {
//     x: 12,
//     y: 23,
//     z: 34
//   }
// }

const items = [];
items.push(config['a']);
console.log(items); 
// ->
// [[object Object] {
//   x: 1,
//   y: 2,
//   z: 3
// }]

items[items.length - 1].q = "mutation";
console.log(items); 
// ->
// [[object Object] {
//   q: "mutation",
//   x: 1,
//   y: 2,
//   z: 3
// }]

console.log(config); 
// ->
// [object Object] {
//   a: [object Object] {
//     q: "mutation",
//     x: 1,
//     y: 2,
//     z: 3
//   },
//   b: [object Object] {
//     x: 12,
//     y: 23,
//     z: 34
//   }
// }

console.log(config.a) 
// ->
// [object Object] {
//   q: "mutation",
//   x: 1,
//   y: 2,
//   z: 3
// }

为什么JavaScript会这样设计?

你猜的没错——items[0]是config.a的引用,而不是副本。这是因为JavaScript里的对象(包括数组、普通对象、函数等)属于引用类型,和数字、字符串这类基本类型的存储逻辑完全不同:

  • 基本类型的值直接存在栈内存里,赋值时会复制整个值,两个变量互不影响;
  • 引用类型的值存在堆内存里,变量保存的只是指向堆内存的“地址”(也就是引用)。当你把config.a放进数组时,只是把这个地址存进了数组,所以items[0]和config.a指向的是同一个堆内存对象,修改任何一个都会同步影响另一个。

这种设计本质是为了性能优化:如果每次赋值对象都复制整个对象(尤其是大对象),会占用大量内存,运行速度也会变慢。而引用的方式只传递地址,效率高得多。同时,很多场景下我们也需要这种“共享数据”的特性——比如多个组件操作同一个状态集合,不用手动同步就能保持数据一致。

如何避免修改原config对象?

核心思路是:创建原对象的副本,把副本放进数组,而不是直接放引用。根据对象的嵌套程度,分为两种情况:

1. 浅拷贝(适合单层结构的对象)

如果你的对象没有嵌套(比如config.a里都是基本类型值),用浅拷贝就足够了:

  • 用展开运算符:
    items.push({...config['a']});
    
  • 用Object.assign():
    items.push(Object.assign({}, config['a']));
    

2. 深拷贝(适合多层嵌套的对象)

如果对象有嵌套结构(比如config.a里还有子对象),浅拷贝只会复制外层对象的引用,嵌套的对象还是会共享。这时候需要深拷贝:

  • 最简单的方法:JSON.parse(JSON.stringify())
    这个方法能快速实现深拷贝,但有局限性:不能拷贝函数、Symbol、undefined,遇到循环引用的对象会报错。
    items.push(JSON.parse(JSON.stringify(config['a'])));
    
  • 手写递归深拷贝函数(处理复杂场景):
    如果需要支持函数、Symbol等特殊类型,可以自己写一个递归函数:
    function deepClone(obj) {
      // 处理基本类型和null
      if (obj === null || typeof obj !== 'object') return obj;
      // 处理Date对象
      if (obj instanceof Date) return new Date(obj);
      // 处理数组
      if (obj instanceof Array) return obj.map(item => deepClone(item));
      // 处理普通对象
      const clonedObj = {};
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          clonedObj[key] = deepClone(obj[key]);
        }
      }
      return clonedObj;
    }
    
    // 使用方式
    items.push(deepClone(config['a']));
    

修改后的代码示例(用深拷贝):

const config = {a: {x: 1, y: 2, z: 3}, b: {x: 12, y: 23, z: 34}};
console.log(config);

const items = [];
// 放入深拷贝的副本
items.push(JSON.parse(JSON.stringify(config['a'])));
console.log(items);

items[items.length - 1].q = "mutation";
console.log(items); // 副本被修改
console.log(config); // 原config完全不受影响!
console.log(config.a); // 原对象保持初始状态

内容的提问来源于stack exchange,提问作者sudo bangbang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:04:14