JavaScript循环向对象数组push元素仅最后一项生效问题
问题根本原因
两个核心问题共同导致了你遇到的异常:
- 对象引用传递特性:JavaScript中对象属于引用类型,你在循环外预先声明了唯一的
this.locationObject空对象,后续循环中每次修改属性、push到数组的操作,本质上都是在操作同一个内存地址的对象,数组中存储的所有元素都只是指向这一个对象的引用。循环执行完成后,这个对象的属性是最后一次循环赋值的结果,自然所有数组项看起来都被最后一次的值覆盖。 - 异步回调的上下文错位:你尝试在循环开头重置
that.locationObject = {}的方案,确实解决了引用共享的问题,但Ajax的success回调是异步执行的——等所有请求触发回调时,for循环早已执行完毕,此时that.locationObject已经指向了最后一次循环创建的新对象。所有回调执行时都会给这最后一个对象赋值pos属性,之前循环中push进数组的独立对象根本没有被设置pos,最终就出现了tooltip、type正常但pos缺失的问题。
可直接运行的修复代码
核心调整思路:每次循环创建独立的局部对象,通过闭包让每个Ajax回调绑定到当前迭代对应的对象上,不要在实例this上挂载共享的临时对象。
this.locationObjectArray = []; var that = this; oModel.read("/Customers", { success: function (resp) { var promises = []; // 移除循环外的共享locationObject声明,不要在this上挂载临时对象 for (var x = 0; x < resp.results.length; x++) { // 每次循环创建独立的当前位置对象,用局部变量存储 var currentLocation = { tooltip: resp.results[x].Country, type: "Inactive" }; // 发起地理编码请求 var request = $.ajax({ url: "https://api.opencagedata.com/geocode/v1/json?key=d3e40bf9b50247d4acf8122c543c5187&q=" + currentLocation.tooltip, type: "GET", dataType: 'json', // 闭包直接持有当前循环的currentLocation引用,不会出现上下文错位 success: function (geoResp) { currentLocation.pos = geoResp.results[0].geometry.lng + ";" + geoResp.results[0].geometry.lat + ";0"; } }); // 推入当前独立对象、收集promise that.locationObjectArray.push(currentLocation); promises.push(request); } // 所有请求完成后再更新模型 $.when.apply(null, promises).done(function(){ that.getView().getModel("map").setProperty("/Spots", that.locationObjectArray); }) } })
额外优化建议
- 如果你的运行环境支持ES6语法,可以把循环中的
var x改成let x,配合let的块级作用域特性,可以进一步避免循环变量的上下文共享问题 - 不建议把流程中用到的临时中间对象挂载到组件实例
this上,临时变量用函数内局部变量存储即可,避免全局污染和意外的引用篡改 - 可以给Ajax请求增加错误回调,避免某一个地理编码请求失败导致整个
$.when链直接挂掉
内容的提问来源于stack exchange,提问作者SeventhWarhawk
相关产品推荐
相关产品推荐

