Javascript的Scope如何工作,获得意外行为?

时间:2014-01-08 08:43:03

标签: javascript scope

下面是一些代码,类似于让我悲伤的代码:

for(var i=0;i<3;i++){
    var Proof = true
    if (i == 0){
        Proof = false
        //var CallBack = function(){alert(Proof);}; // true????
        var CallBack = function(){alert(i);};
    }
}

// Whatever happened to local scope? shouldn't CallBack be undefined now?
CallBack()// alert(3), should be 0?

CallBack没有未定义,它提醒真实或3,有人可以向我解释发生了什么事情使它像这样做?我缺少一些本地关键字,还是JS没有范围?我没有使用任何框架,并希望得到一些帮助,如何解决这个问题,并正确地做这件事...

2 个答案:

答案 0 :(得分:2)

JavaScript根本没有块范围(但是,见下文)。所有变量都在它们出现的函数中声明(或者在整个全局范围内,如果它们在全局范围内)。

假设您引用的代码是其范围内唯一的代码,则完全与此相同:

var i;
var Proof;
var Callback;

for(i=0;i<3;i++){
    Proof = true
    if (i == 0){
        Proof = false
        //CallBack = function(){alert(Proof);}; // true????
        CallBack = function(){alert(i);};
    }
}

// Whatever happened to local scope? shouldn't CallBack be undefined now?
CallBack()// alert(3), should be 0?

更多(在我的博客上)Poor, misunderstood var


  

CallBack()// alert(3), should be 0?

不,3是正确的,因为这是i 被称为CallBack的值。 CallBack是对创建它的上下文的闭包。它对上下文(包括i变量)有持久引用,而不是创建它时存在的副本。

如果您想获得0,则必须CallBack关闭除i以外的其他内容,这些内容不会发生变化。执行此操作的典型方法是使用将值传递到的构建器函数:

function buildCallBack(index) {
    return function() { alert(index); };
}

然后:

CallBack = buildCallBack(i);

i的值作为参数buildCallBack传递到indexbuildCallBack创建一个函数,该函数关闭对buildCallBack的调用的上下文并使用该index参数。由于该参数的值永远不会更改,因此回调会警告0(在本例中)而不是3

更多(在我的博客上)Closures are not complicated


我上面说“尚未”的原因是规范的下一个版本(ES6)将为新的letconst关键字以及块函数声明引入新的block-scope semanticsvar将保持不变,但如果使用let来声明变量,则它具有块范围而不是函数/全局范围。

答案 1 :(得分:1)

当调用CallBack时,for循环已经完成。所以i等于3。

如果您希望i是本地的,您应该这样写:

var CallBack;
for(var i=0;i<3;i++){
    (function(index) {
        var Proof = true
        if (index == 0){
            Proof = false
            //var CallBack = function(){alert(Proof);}; // true????
            CallBack = function(){alert(index);};
        }
    })(i);
}

CallBack()

这是demo

<强>更新

这是另一种选择:

var CallBack;
for(var i=0;i<3;i++){
    var Proof = true
    if (i == 0){
        Proof = false
        CallBack = (function(index) {
            return function(){alert(index);};
        })(i);
    }
}

CallBack()

demo

更新2

阐释:

通过编写(function(index) {...})(i)我们声明一个匿名函数并立即调用它(我们也可以编写function(index) {...}(i)但我认为第一种情况更清楚)。在javascript中,function也是一种设置另一个变量范围的方法,因此在我们的例子中,我们“记住”i变量中index变量的当前状态,该变量仅在我们的变量中可见匿名函数。

当调用'CallBack'时,alert显示0,因为i在调用该函数时等于0