两个个类对象指向同一个地址
两个变量指向同一个对象,个类改一个,对象地址另一个也跟着变。个类这不是对象地址bug,是个类引用类型的基本行为。
上周帮同事看代码,对象地址他写了这么一段:
let a = { count: 0 }; let b = a; b.count = 5; console.log(a.count); // 5 他以为 let b = a 会复制一份出来,个类结果 a.count 也变成了5。对象地址他盯着屏幕看了半天,个类说“这不科学”。对象地址其实很科学,个类只是对象地址和直觉拧着来。
变量里存的个类不是对象本身,是对象地址一个地址。你可以把变量想象成一张纸条,个类上面写着一间房的房号。let b = a 做的事,是把 a 那张纸条上的房号抄到 b 的纸条上。两张纸条,同一个房号。你拿 b 的纸条找到房间,把里面的沙发换了,a 的纸条找到的是同一个房间,沙发当然也换了。
JavaScript 里所有对象——包括数组、函数、普通对象——都是引用类型。赋值操作符 = 对它们来说,只传递地址,不复制内容。原始类型(数字、字符串、布尔值)才真正复制值,因为它们的“值”就写在纸条上,不是房号。
这个机制在函数传参时更隐蔽:
function addItem(arr) { arr.push('new'); } const list = ['old']; addItem(list); console.log(list); // ['old', 'new'] 函数内部没有 return,但外部数组变了。因为传进去的是地址,函数里 push 的是同一个数组。
有人会说“那我重新赋值不就行了”——
function reset(arr) { arr = []; } const list = [1, 2, 3]; reset(list); console.log(list); // [1, 2, 3] 这次没变。因为 arr = [] 只是把函数内部那张纸条上的房号擦掉,写了一个新房号。外面那张纸条没动,还指着老房间。所以数据没丢。
想真正断开两个变量的联系,得创建新对象。展开运算符、Object.assign、slice、map 这些方法做的事叫浅拷贝——新建一个房间,把老房间的家具搬过去。但注意,如果家具本身也是对象(嵌套引用),搬过去的还是地址。改嵌套对象,两边照样一起变。要彻底断开,得深拷贝,或者用 structuredClone。
我见过一个真实事故。某电商后台,购物车对象存在全局状态里,一个模块直接 const cart = globalCart,然后修改 cart.items。另一个模块也在读 globalCart,发现数据莫名其妙变了,排查了一下午。问题不在逻辑,在那一行赋值。
Python 里也一样。a = [1,2,3]; b = a; b.append(4),a 也变成 [1,2,3,4]。Java 里数组和对象同理,但 String 是特例,不可变,看起来像值传递。C 的指针就更直白了,地址摆在那里,谁都能顺着改。
这个行为本身没有好坏。它省内存,让多个地方共享同一份数据,改一处全局生效。但如果你没意识到自己在共享,就会觉得程序在跟你作对。写代码时多问一句:我这一行,是复制了房号,还是盖了新房子?
本文地址:http://www.79wt.cn/news/894c9699009.html
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。