Javascript 变量作用域和事件处理程序

Javascript 变量作用域和事件处理程序,javascript,scope,event-handling,dom-events,Javascript,Scope,Event Handling,Dom Events,请参阅JSFIDLE: 函数MyFunc(){ 对于(var i=0;i

请参阅JSFIDLE:

函数MyFunc(){
对于(var i=0;i<2;i++){//i=0,1
var myDiv=$('');
myDiv.click(函数(e){
警报(i);//两个div都警报“2”,而不是我预期的0和1
});
$('body').append(myDiv);
}
}
var myFunc=新的myFunc();
我希望在单击div时,它们分别向“0”和“1”发出警报,但它们都向“2”发出警报

当我单击div并触发事件时,处理程序如何以及在何处查找变量
I


我知道添加闭包可以实现我的目标。但是为什么呢?

您需要一个闭包,因为所有事件处理程序函数都引用同一个变量
i
for
循环将更新此值,循环完成后,变量包含
2
。然后,当有人单击其中一个div时,它将访问该变量

要解决这个问题,每个事件处理程序都需要是一个闭包,它有自己的变量
i
,该变量包含创建闭包时值的快照。

函数MyFunc(){
    function MyFunc() {

    for (var i = 0; i < 2; i++) { // i= 0, 1
        (function(j) {
            var myDiv = $('<div>');

            myDiv.click(function(e) {
                alert(j);
            });
            $('body').append(myDiv);
        })(i);
    }
}

var myFunc = new MyFunc();
对于(var i=0;i<2;i++){//i=0,1 (职能(j){ var myDiv=$(''); myDiv.click(函数(e){ 警惕(j); }); $('body').append(myDiv); })(i) ); } } var myFunc=新的myFunc();
上面的代码是如何让它正常工作的。如果没有闭包,则始终是i的最后一个值。我们要做的是将i发布到闭包中,让运行时“记住”那个时刻的值。

我建议您阅读

JavaScript提升声明。这意味着两个var语句 函数声明将被移动到它们的封包的顶部 范围

正如@Barmar在上面的回答中所说,变量
i
正被两个事件处理程序引用

应该避免在循环中声明函数。下面有一些代码可以满足您的需要

我假设您正在使用jQuery

function MyFunc() {

    for (var i = 0; i < 2; i++) { // i= 0, 1
        var myDiv = $('<div>');

        $('body').append(myDiv);
    }
    $('div').on('click', function() {
        alert($(this).index());
    });
}

var myFunc = new MyFunc();
函数MyFunc(){
对于(var i=0;i<2;i++){//i=0,1
var myDiv=$('');
$('body').append(myDiv);
}
$('div')。在('click',function()上{
警报($(this.index());
});
}
var myFunc=新的myFunc();
调用“alert()”发生在for循环完成之后,这意味着“i”的值将是该循环完成之后的最后一个值。为了捕获“i”的单个值,必须通过创建新函数为每个值创建闭包:

function MyFunc() {

    function alertFn(val) {
        return function () {
            alert(val);
        };
    }

    for (var i = 0; i < 2; i++) {
        var myDiv = $('<div>');
        myDiv.click(alertFn(i));
        $('body').append(myDiv);
    }
}

var myFunc = new MyFunc();
函数MyFunc(){
功能警报FN(val){
返回函数(){
警报(val);
};
}
对于(变量i=0;i<2;i++){
var myDiv=$('');
myDiv.click(警报fn(i));
$('body').append(myDiv);
}
}
var myFunc=新的myFunc();

闭包在传递到函数时捕获“i”的值,从而允许alert()显示您期望的值。

这是标准方法吗?如果循环体相当长,我们是否仍将其包装在匿名函数中?这是常用的习惯用法。但是如果你喜欢更长的函数,你可以使用Zenwolf的风格。我可以看到人们通常是这样写的。尽量不要在大循环中这样做。我的意思是喜欢(var I=0;I<1000;I++)。你说你知道正确的编写方法,你只是想解释一下为什么需要这样做。我不确定我能写什么代码来说明答案——你的代码做得很好。
function MyFunc() {

    function alertFn(val) {
        return function () {
            alert(val);
        };
    }

    for (var i = 0; i < 2; i++) {
        var myDiv = $('<div>');
        myDiv.click(alertFn(i));
        $('body').append(myDiv);
    }
}

var myFunc = new MyFunc();