如何使用Javascript';s范围工作,获得意外行为?

如何使用Javascript';s范围工作,获得意外行为?,javascript,scope,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 t

下面是一段代码示例,类似于给我带来悲伤的代码:

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?

for(var i=0;iJavaScript根本没有块作用域(但是,请参见下文)。所有变量都在其出现的函数中声明(或者在全局作用域中声明,如果它们在全局作用域中)

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

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?
然后:

i
的值被传递到
buildCallBack
中,因为参数
index
buildCallBack
创建了一个函数,该函数在调用
buildCallBack
的上下文上关闭,并使用该
index
参数。由于该参数的值从未更改,因此回调会发出警报
0
(在本例中)而不是
3

更多(在我的博客上):



我在上面说“尚未”的原因是规范的下一个版本(ES6)将为新的
let
const
关键字和块函数声明引入new。
var
将保持不变,但如果使用
let
声明变量,则它具有块作用域而不是函数/全局作用域。

调用回调时,
for
循环已完成。So
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()
var回调;

对于(var i=0;对于回调的堆栈,iShouldn至少不应该应用pass-by-copy吗?或者pass-by-copy只适用于直接变量赋值,而且我已经在不理解的情况下编写了大量Js代码,这也有点可怕this@JohnDoe:什么传递副本?问题似乎是关于作用域,而不是函数调用。对不起,在Lua(我的母语)中,局部函数的函数体中携带的变量在其自己的作用域中声明(每个作用域到达is时都有变量的“副本”)因此,即使稍后调用该函数,该函数的变量“副本”仍然是active@JohnDoe:我明白了。我刚刚注意到代码中的注释,其中询问为什么警报显示为3而不是0,因此我在答案中添加了此注释以解决此问题。感谢您解释缺少范围:-)虽然Zub的答案似乎更吸引人,但因为我的应用程序,我更看重语法的简洁性,而不是速度。通常来说,让回调的生成器是一个单独的、命名的、可重用的函数更好,特别是在循环的情况下。否则,除了清晰性问题外,您还需要创建和抛出无缘无故地重复删除构建器函数。用线程对其进行黑客攻击是一个有趣的想法,但为什么要将函数封装在()中?您如何将i传递给它,使其看起来像一个调用,但似乎不是“(function{})(i)”?@johndoe,方法是编写
(function(i){…})(i)
我声明函数并立即调用它。我传递
I
并且
I
变量出现在函数体中(我可以用不同的名称命名)。内部
I
反映当前的外部
I
state@T.J.克劳德用建筑工人是个好主意。我没有想到it@JohnDoe:“…函数(){}()对我来说似乎比…”更清楚,“只要你已经在表达式中(在你的例子中,你是在
=
的右侧),你就可以这样做,没问题。有时你需要这些外部参数来避免解析器将表达式当作函数声明来对待。
CallBack = buildCallBack(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()
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()