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

JavaScript数组push对象是异步操作吗?值重复是何原因

问题解答

首先明确:Array.push() 是完全同步的操作,你遇到的结果异常和异步没有任何关联,本质是JavaScript引用类型的存储特性导致的bug。

根因说明

JavaScript中,对象属于引用类型,变量存储的是对象在堆内存中的地址,而不是对象本身的完整值。
原代码在循环外只声明了一个共用的obj对象,整个循环执行过程中操作的始终是同一块内存地址上的同一个对象:

  1. 第一次遍历到值为2的元素时,匹配到之前存过的补数8,修改这个公共obj的属性为{first:8, second:2},再把obj的引用push进结果数组——此时数组里存的不是obj的快照拷贝,只是指向这个公共obj的内存地址
  2. 循环继续执行,遍历到值为3的元素时,匹配到之前存过的补数7,再次修改同一个公共obj的属性为{first:7, second:3},再把同一个内存地址push进结果数组
  3. 循环结束后,结果数组里的两个元素本质是两个指向同一个公共obj的指针,最后一次修改obj属性的操作会覆盖之前的赋值,所以最终打印出来两个元素都是{first:7, second:3}

原问题逻辑的核心问题代码如下:

// 循环外仅创建1个对象,所有操作都基于这一个对象
let obj = {
    first: 0,
    second: 0
}

for(let i=0; i<arr.length; i++){
    let comp = target - arr[i];
    if(mappingArr.has(comp)){
        // 每次都是修改同一个对象的属性,没有生成新对象
        obj.first = arr[mappingArr.get(comp)];
        obj.second = arr[i];
        // push的是同一个对象的引用
        res.push(obj)
    } else {
        mappingArr.set(arr[i], i)
    }
}

原代码预期输出:[{first: 8, second: 2},{first: 7, second: 3}]
原代码实际输出:[{first: 7, second: 3},{first: 7, second: 3}]

修改后代码正常的原因

调整为在push时直接传入对象字面量的写法后,每次进入匹配判断分支,都会创建一个全新的独立对象,每个新对象都占用单独的内存空间,push进数组的是不同对象的独立引用,互相之间没有关联,后续的操作不会修改之前已经存入数组的对象内容,因此运行结果符合预期。

修改后的核心逻辑:

if(mappingArr.has(comp)){
    // 每次命中匹配都生成全新的独立对象,不存在引用复用问题
    res.push({first: arr[mappingArr.get(comp)], second: arr[i]});
} else {
    mappingArr.set(arr[i], i)
}

拓展说明

这个问题不是JavaScript独有,Java、C#、Python等所有区分值类型和引用类型的编程语言,都会出现同类问题:只要向集合中存入引用类型的值,默认存入的都是内存地址,而非值的完整拷贝。如果后续修改原引用指向的内存内容,集合中读取到的值也会同步变化。
如果一定要保留提前声明obj的写法,只需要在每次push前为obj重新赋值一个全新对象,切断和之前对象的引用关联即可,示例写法:

if(mappingArr.has(comp)){
    // 每次赋值为新对象,覆盖原有引用
    obj = {
        first: arr[mappingArr.get(comp)],
        second: arr[i]
    }
    res.push(obj)
}

内容的提问来源于stack exchange,提问作者Krutarth Dave

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:21:50