JavaScript forEach遍历对象数组时为何能直接修改原数组元素?
问题描述
你定义了如下存储人员信息的对象数组:
const people = [ { name: "John", dateOfBirth: 1990, dateOfDeath: 2020 }, { name: "Jane", dateOfBirth: 1970, dateOfDeath: 2019, }, { name: "Micheal", dateOfBirth: 2002, } ]
需求为遍历整个数组,为所有不存在dateOfDeath属性(即仍然在世)的人员对象,将其dateOfDeath属性设置为当前年份。你编写了如下代码实现需求:
people.forEach ( element => { if(!("dateOfDeath" in element)) element.dateOfDeath = (new Date()).getFullYear(); });
你的核心疑问:
为什么上述代码可以正常生效?回调箭头函数是如何访问并修改数组内元素的?按照常规理解,element作为回调函数的参数属于函数内的局部变量,对该变量的修改理应不会影响原数组中的元素,为什么实际运行时却修改了原数组的对应对象?
解答
这是JavaScript引用类型的基础特性,核心原因是你混淆了「修改变量本身的绑定」和「修改变量指向的对象属性」两种完全不同的操作:
- JavaScript中对象属于引用类型,数组存储对象类型值时,存的不是对象的完整内容,而是指向堆内存中实际对象的引用地址(可以理解为指向对象存储位置的指针)。
forEach方法遍历到对应数组项时,会把数组当前索引位存储的引用地址拷贝一份,传给回调函数的形参element。此时函数内的局部变量element,和原数组对应位置存储的引用,指向堆内存里同一个对象实例。- 如果你在回调中直接给
element变量重新赋值,比如写element = {name: "test"},这只是改变了局部变量element存储的引用指向,让它指向一个全新的对象,这种操作完全不会改动原数组中存储的引用地址,原数组不会受到任何影响。 - 但你代码中执行的是
element.dateOfDeath = xxx,这个操作根本没有修改element变量本身存储的引用地址,而是顺着这个地址找到堆内存里的原对象,直接给这个对象新增/修改属性。这个对象本身就是原数组引用的对象,修改结果自然会直接同步反映到原数组上。
补充说明:你最初写的示例代码中new date()存在大小写错误,Date构造函数首字母必须大写才能正常运行,可生效的正确写法为new Date().getFullYear()
内容的提问来源于stack exchange,提问作者Lorenzo Zabot
相关产品推荐
相关产品推荐

