如何在QueryKeyword回调函数中获取for循环的j变量值?
解决异步回调中获取循环变量j的正确值问题
这个问题是典型的闭包在异步循环中的陷阱——你的QueryKeyword里的ajax请求是异步执行的,而循环里的j是用var声明的共享变量(函数作用域),当ajax的success回调触发时,外层的for循环早就执行完毕了,此时j已经变成了site.length,所以每次打印出来的都是同一个错误值。
下面给你三种可行的解决方案:
方案一:用立即执行函数表达式(IIFE)创建独立作用域
通过IIFE把当前迭代的j值封装到独立的作用域里,让回调函数捕获的是每个迭代独有的变量:
for (i = 0; i < questionTerm.length; i++) { for (j = 0; j < site.length; j++) { // 用IIFE保存当前的j值 (function(currentJ) { var searchTerm = questionTerm[i] + ' ' + $('#search').val(); QueryKeyword(searchTerm, site[currentJ], function(reslt) { console.log(currentJ); // 这里拿到的是当前迭代的j值 }); })(j); } }
原理:IIFE会立即执行,把当前循环的j值作为参数currentJ传入,这个currentJ是每个迭代独立的变量,不会被后续循环的j值覆盖。
方案二:使用ES6的let声明循环变量
如果你的运行环境支持ES6,这是最简单的方法——把内层循环的j用let声明,let会在每次循环迭代时创建一个新的块级作用域变量,回调函数会绑定当前迭代的那个j:
for (i = 0; i < questionTerm.length; i++) { // 用let声明j,每个迭代都有独立的j变量 for (let j = 0; j < site.length; j++) { var searchTerm = questionTerm[i] + ' ' + $('#search').val(); QueryKeyword(searchTerm, site[j], function(reslt) { console.log(j); // 现在能拿到正确的j值了 }); } }
原理:let在循环中具有块级作用域特性,每次循环的j都是全新的变量,回调引用的是当前迭代的j,自然不会出现变量覆盖的问题。
方案三:给QueryKeyword添加额外参数传递j值
修改你的QueryKeyword函数,让它接受一个额外的参数来保存当前的j值,然后在ajax成功时把这个值一起传给回调:
首先修改QueryKeyword函数:
function QueryKeyword(keyword, site, currentJ, callback) { var querykeyword = keyword; var website = site; $.ajax({ url: "http://www.test.com", jsonp: "jsonp", dataType: "jsonp", data: { Query: querykeyword }, success: function(reslt) { // 把保存的currentJ和结果一起传给回调 callback(reslt, currentJ); } }); }
然后调用时传入当前的j:
for (i = 0; i < questionTerm.length; i++) { for (j = 0; j < site.length; j++) { var searchTerm = questionTerm[i] + ' ' + $('#search').val(); // 把j作为第三个参数传入 QueryKeyword(searchTerm, site[j], j, function(reslt, currentJ) { console.log(currentJ); // 这里拿到正确的j值 }); } }
原理:这种方法通过参数传递的方式保存了当前j的数值(按值传递),完全避开了闭包引用的问题,适合不想修改循环写法的场景。
内容的提问来源于stack exchange,提问作者Dhaval
相关产品推荐
相关产品推荐

