如何在传递回调函数时绑定变量值?解决绑定失效问题
嘿,我来帮你理清楚这个问题~你现在遇到的情况其实是变量作用域和闭包延迟执行搞的鬼,再加上对.bind()的用法有点小误解。
为啥你的代码会输出三个3?
首先,你循环里的i没加let/const,默认是全局变量。当setTimeout的回调函数执行时,循环早就跑完了,i已经变成3了。另外,你用.bind(i)其实是把i绑定成了回调函数的this,不是作为参数传递——这也是个关键误区,.bind()的第一个参数是用来绑定this的,后面的参数才是要传给回调的前置参数。
几种靠谱的解决方案
1. ES6+最简洁:用let声明循环变量
把循环里的i换成let,这样每次循环都会创建一个独立的作用域,回调函数会捕获当前循环的那个i值,完美解决问题:
function callbackTest() { for(let i=0; i<3; i++){ setTimeout(function(){ console.log("Iteration number ", i); }, i*1000); } } callbackTest();
执行后会依次输出Iteration number 0、Iteration number 1、Iteration number 2(符合你的预期)。
2. 兼容ES5:用立即执行函数(IIFE)创建闭包
如果要支持旧环境,用立即执行函数把每次循环的i封装成独立作用域,让回调捕获这个不会变的局部变量:
function callbackTest() { for(var i=0; i<3; i++){ // 把当前的i传给currentI,创建独立作用域 (function(currentI) { setTimeout(function(){ console.log("Iteration number ", currentI); }, currentI*1000); })(i); } } callbackTest();
3. 正确使用.bind()传递参数
如果你想继续用.bind(),得搞对用法:第一个参数传null(不需要绑定this的话),后面的参数才是要传给回调的数值:
function callbackTest() { for(var i=0; i<3; i++){ setTimeout(function(currentI){ console.log("Iteration number ", currentI); }.bind(null, i), i*1000); // 这里的i会被绑定成回调的第一个参数currentI } } callbackTest();
4. ES6+箭头函数配合let
箭头函数会捕获当前上下文的变量,结合let用也很简洁:
function callbackTest() { for(let i=0; i<3; i++){ setTimeout(() => console.log("Iteration number ", i), i*1000); } } callbackTest();
总结
最推荐的是第一种let的写法,简洁直观,是ES6专门为这类循环闭包问题设计的特性。如果要兼容旧环境,IIFE或者正确的.bind()用法都可以。
内容的提问来源于stack exchange,提问作者Wes Modes
相关产品推荐
相关产品推荐

