Function declarations in unreachable conditional blocks
很有意思。 你为什么这么认为?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var fn1 = function() {
function fn2() {
return"fn2 initialize...."
}
if (false) {
function fn2() {
return"fn2 if --> false"
}
}
return fn2();
}
fn1(); //"fn2 if --> false" |
或者更有趣;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var fn1 = function() {
function fn2() {
return"fn2 initialize...."
}
if (false) {
function fn2() {
return"fn2 if --> false"
}
}
return fn2();
function fn2() {
return"fn2 return after"
}
}
fn1(); //"fn2 return after" |
-
你的问题是什么?这里发生的是JavaScript将函数定义提升到函数范围的顶部。 JavaScript没有块范围 - 因此if语句不会形成自己的范围。 if语句中的函数被提升到闭包函数的顶部。解决方案?不要使用函数声明(改为使用函数表达式),也不要在非函数块中定义任何var。将每个函数的所有var声明放在该函数的顶部,因此提升是显式的。
-
@Cuberto:"if语句中的函数被提升到闭包函数的顶部。"不必要。有些引擎拒绝整个事情作为语法错误(根据当前规范,它是一个);其他人将声明重写为表达式,而不是将它们挂起。 Firefox(mumble)猴子引擎很长一段时间拒绝了这些(现在它将它们重写为表达式)。
-
"你为什么这么认为?"因为我喜欢它 :)
-
@ T.J.Crowder啊,无论如何,使用我评论第二部分中的建议可以避免这种情况。
-
@Cuberto:的确如此。 :-)
-
相关问题:May函数声明出现在JavaScript中的语句中吗?你一定应该阅读接受的答案,这是围绕这个问题的有趣解释。
-
我很乐意对这个问题进行投票,但我认为应该强调这个问题。不是每个人都知道JS引擎如何工作和解释代码示例,因此不清楚要问的是什么。
该代码中发生了两件事,其中一件是指定的行为,另一件在语法上无效(现在),其结果因JavaScript引擎而异。
无效位是您不能在条件块中具有函数声明。例如,这个位无效:
1 2 3 4 5
| if (false) {
function fn2() {
return"fn2 if --> false"
}
} |
一些引擎会将其视为函数声明,这意味着它不受逐步代码流的影响(因为函数声明不是,它们发生在逐步流程之前)。
其他引擎将(实际上)重写为您的函数表达式,将其置于逐步流程中。
我相信ECMAScript6将解决这个问题。
指定的位涉及在同一范围内只有两个声明,例如:
1 2 3 4 5 6 7 8 9 10 11 12
| var fn1 = function() {
function fn2() {
return"the first fn2"
}
return fn2();
function fn2() {
return"the second fn2"
}
};
fn1(); //"the second fn2" |
规范明确规定范围内的所有函数声明都按源代码的顺序处理,因此上面(删除了无效位)可靠地使用第二个fn2,而不是第一个。
-
谢谢你的回答。
-
您可能希望在阅读(再次?)FunctionExpression和内存消耗后再查看"语法无效"这一短语。 ECMAScript中允许使用函数语句,除非您在ES5中有一个排除它们的引用。
-
@RobG:你指的是哪个部分?从我读到的,函数声明不是语句,因此在块内无效(stackoverflow.com/a/4071439/218196)。 ES5规范证实了这一点:es5.github.io/#x14(注意声明和功能声明是如何单独列出的)。
-
我指的是理查德帖子的部分,他说In fact it is a function statement; a syntax extension that is capable of allowing the conditional creation of a function because being a Statement it can be [evaluated] inside a block. ES5也识别出警告它的函数语句:已知几种广泛使用的ECMAScript实现支持将FunctionDeclaration用作Statement。因此,虽然他们肯定要避免,但它们在语法上并不是不正确的。
-
@RobG:哦,我明白了。但是我认为规范中的这一部分实际上意味着实现错误地将声明视为语句,因此它在语法上是不正确的,至少根据规范。
-
@RobG:我认为你误解了§ 12的那一部分。它不是"识别"它们(在将它们定义为规范的一部分的意义上)。它承认有一些实现支持非指定的无效行为,并建议不要这样做,直到规范解决它(它可能会在ES6中)。实际上,§ 12非常清楚函数声明不是语句,因为它给出了一个语句在该部分开头的正确列表,并且FunctionDeclaration不在该列表中。
-
@ T.J.Crowder:如果我能像你一样表达我的想法:)我总是喜欢读你写的东西。节日快乐!
-
@RobG:FWIW,我的猜测是,一旦有时间到达那里,FunctionStatement就会显示在ES6中,并且它会执行Firefox(mumble)Monkey当前所做的事情:将它们有效地视为命名函数表达式。 (我肯定会有一些簿记让他们不是非常的NFE,但......)
-
@FelixKling:哈哈,你呢!
-
引用的要点是浏览器可能将{ function foo(){} }视为语句而不是FunctionDeclaration,而ES5并未将其视为非法或语法不正确。如果是的话,就没有必要提及它们,因为预计确认实施会产生错误。
-
@RobG:是的,规范确实说这样做是无效的,因为没有在语句列表中列出FunctionDeclaration(或者更可能是一种形式的FunctionStatement)并定义它们的语义。将函数声明作为语句处理就像将glarb视为this的同义词一样。关于函数声明的部分中没有任何地方允许它们作为逐步执行代码的一部分发生。这样做无效。它可能是有用的,或者如果引擎同意该怎么做,但这并不符合规范。
-
@ T.J.Crowder-该规范正在承认它们的存在,并且可能在那时宣布它们是非法的或无效的或其他什么,但事实并非如此。它们不允许在严格模式下使用。请注意,foo : function bar(){alert('bar')}是函数语句的另一个(较少引起争议的)情况(Brendan Eich将其称为"带标签的命名函数表达式语句")。在严格模式下也不允许这样做。
-
@ T.J.Crowder-如果他们根本不被允许那么浏览器会出现错误,并且根本不需要在ES5中提及它们。在严格模式下明确禁止函数语句。如果不允许他们采用非严格模式,则无需这样做。
-
@RobG:没见"嘿,看看这个。不要这样做,这是违法的"并不会产生符合规范的行为。 :-)和(mumble)Monkey确实曾经在这些问题上抛出语法错误;现在它预计下一个规格。我正在打电话给我。很明显,如果你阅读规范没有函数声明,并且没有指定使用函数声明作为语句,所以这样做完全是不合规格的。可能会出现在下一个规范中。与此同时,一些引擎做了一些非常有用的东西。发动机做了很多这样的事情。
-
@ T.J.Crowder-我没有说他们在规范中,但他们被允许作为语法扩展。 ES5明确提到他们并认为由于支持不一致而适合警告他们,但确实说"这些是非法的",因为他们不是。如果他们根本不被允许,为什么他们在严格模式下被明确禁止(你的"嘿,看看这个"时刻)? ECMAScript允许函数语句作为语法扩展。 Brendan Eich的语言规范参考和引用应该足以证明这一点。