函数声明可以出现在 JavaScript 的语句中吗?
Posted
技术标签:
【中文标题】函数声明可以出现在 JavaScript 的语句中吗?【英文标题】:May function declarations appear inside statements in JavaScript? 【发布时间】:2011-05-03 13:06:37 【问题描述】:请考虑将官方 ECMAScript 规范作为答案的来源,而不是特定浏览器供应商发布的文档。 (我知道 Mozilla 用“函数语句”扩展了它的 javascript 实现。)
那么,根据 ECMAScript 规范,ergo,其中定义的语法产生式,这有效吗?
if (foo)
function x() return;
更新: 我的问题也可以这样表述:Statement 产生式是否包含 FunctionDeclaration 产生式?
结论:答案是否定的。
【问题讨论】:
仍然应该如此。规范很烂。 @MooGoo,不,规范并不糟糕。在块中使用函数表达式。 【参考方案1】:我不同意其他说它有效的答案。
根据ECMA-262 5th Edition specification,Blocks
只能包含Statements
(Section 12.1):
Block :
StatementList opt
StatementList :
Statement
StatementList Statement
但是规范没有定义函数语句,而只定义了FunctionDeclaration
和FunctionExpression
。该规范进一步在Section 12 中对此进行了说明:
众所周知,一些广泛使用的 ECMAScript 实现支持将
FunctionDeclaration
用作Statement
。然而,在应用于此类FunctionDeclarations
的语义中,实现之间存在显着且不可调和的变化。由于这些不可调和的差异,将FunctionDeclaration
用作Statement
会导致代码在实现之间不能可靠地移植。建议 ECMAScript 实现要么禁止FunctionDeclaration
的这种用法,要么在遇到这种用法时发出警告。未来版本的 ECMAScript 可能会定义替代的可移植方式,用于在Statement
上下文中声明函数。
如需进一步阅读,您可能也有兴趣查看comp.lang.javascript FAQ Section 4.2:
4.2 什么是函数语句?
术语函数语句已被广泛且错误地用于描述
FunctionDeclaration
。这是一种误导,因为在 ECMAScript 中,FunctionDeclaration
不是Statement
;程序中有一些地方允许使用Statement
,但不允许使用FunctionDeclaration
。为了增加这种混淆,一些实现,特别是 Mozillas 的实现,提供了一个称为函数语句的语法扩展。 ECMA-262 第 3 版和第 5 版第 16 节允许这样做。非标准函数语句示例:
// Nonstandard syntax, found in GMail source code. DO NOT USE. try // FunctionDeclaration not allowed in Block. function Fze(b,a)return b.unselectable=a /*...*/ catch(e) _DumpException(e)
使用函数语句的代码有三种已知的解释。一些实现按顺序将
Fze
作为语句处理。其他人,包括 JScript,在进入它出现的执行上下文时评估Fze
。还有其他人,特别是 DMDScript 和 BESEN 的默认配置,抛出一个SyntaxError
。为了实现跨实现的一致行为,不要使用函数语句;请改用
FunctionExpression
或FunctionDeclaration
。FunctionExpression 示例(有效):
var Fze; try Fze = function(b,a)return b.unselectable=a; /*...*/ catch(e) _DumpException(e)
函数声明示例(有效):
// Program code function aa(b,a)return b.unselectable=a
【讨论】:
很棒的答案,很好地参考了 ECMAScript 规范 惊人的答案,感谢您提供如此详细的信息,我一直在努力寻找一个体面的答案! +1 我完全同意。不应将函数声明放在块中,尤其是逻辑或流块(if、while、switch 等)。这也是 JSLint/JSHint 规则。 请参阅ECMAScript 2015 §13.2,它明确允许声明为 StatementListItem。块中允许函数声明(并且一直如此)。唯一的问题是一个浏览器决定将块中的声明视为语句,这是允许的。 自 2010 年 11 月(发布此答案的时间)以来已经过去了一段时间。我想知道从那以后规格是否发生了变化?【参考方案2】:我不知道怎么读,但ECMA-262 V5 有这样的说法:
注意 众所周知,ECMAScript 的几个广泛使用的实现支持将
FunctionDeclaration
用作语句。然而,在应用于此类 FunctionDeclarations 的语义中,实现之间存在显着且不可调和的变化。由于这些不可调和的差异,将 FunctionDeclaration 用作 Statement 会导致代码在实现之间不能可靠地移植。建议 ECMAScript 实现要么禁止 FunctionDeclaration 的这种用法,要么在遇到这种用法时发出警告。 ECMAScript 的未来版本可能会为在 Statement 上下文中声明函数定义替代的可移植方式。
如果我理解正确的话,严格来说,这意味着函数声明根本不能在块内,因为块只能包含语句。
我的解释可能完全错了 - 我不熟悉 ECMAScript 的内部工作原理。
【讨论】:
哈利路亚,有人懂生产规则 @Šime 谢谢!如前所述,我不是这里的专家,所以持保留态度。但@casablanca 所说的似乎证实了这一点 "这意味着函数声明根本不能在块内," 不,它没有。这意味着块内的函数声明不应被视为语句。【参考方案3】:不,这是无效的。函数声明只能作为“源元素”出现,它们要么在全局范围内,要么直接在另一个函数定义中,在所有其他语句之外。来自 ECMA-262 规范:
FunctionBody : 源元素
程序:源元素
SourceElement : 声明 |函数声明
语法中没有其他产生式允许FunctionDeclaration
。
只有函数表达式可以成为语句的一部分:
成员表达式:函数表达式
...
语句:表达式语句
编辑:最近有一个关于另一个问题的相关讨论。请参阅this answer 上的 cmets - 早些时候,我也认为这 可能 是有效的,但语法清楚地表明它是无效的。
【讨论】:
【参考方案4】:From ECMA 262 chapter 14
程序语法
程序:SourceElements 源元素:源元素 源元素源元素 源元素:声明 函数声明语义
制作程序: SourceElements 被评估为 如下:
为函数声明处理 SourceElements。
评估 SourceElements。
返回结果(2)。
生产 SourceElements : SourceElement 被处理为 函数声明如下:
为函数声明处理 SourceElement。
生产 SourceElements : SourceElement 的评估方式如下:
评估 SourceElement。
返回结果(1)。
生产 SourceElements : SourceElements SourceElement 是 处理函数声明为 如下:
为函数声明处理 SourceElements。
为函数声明处理 SourceElement。
生产 SourceElements : SourceElements SourceElement 是 评价如下:
评估 SourceElements。
如果 Result(1) 是突然完成,则返回 Result(1)
评估 SourceElement。
返回结果(3)。
制作SourceElement: *为函数处理语句* 不采取任何行动的声明。
制作SourceElement: *语句评估如下:*
1.评估声明。
2。返回结果(1)。
生产 SourceElement : FunctionDeclaration 被处理为 函数声明如下:
处理函数声明的 FunctionDeclaration(参见第 13 条)。
生产 SourceElement : FunctionDeclaration 被评估为 如下:
返回(正常、空、空)。
遮阳篷正式否。 (Šime Vidas 在另一个问题上说服了我)
但没有指定异常,因此它会失败或静默工作,具体取决于浏览器实现。
【讨论】:
【参考方案5】:ECMA-262 的第 5 版说它不应该是有效的:
仅允许使用函数声明 出现在 Program 或 FunctionBody 中。 从语法上讲,它们不能出现在 块 ( ... ) — 例如 if 的块, while 或 for 语句。这是 因为块只能包含 语句,而不是 SourceElements,它 函数声明是。如果我们看 仔细制作规则,我们可以看到 唯一的表达方式是 Block 内允许的时间是 ExpressionStatement 的一部分。然而, ExpressionStatement 是明确的 定义为不以“功能”开头 关键字,这正是 使 FunctionExpression 无效为 语句或块的一部分(注意 该 Block 只是一个列表 声明)。
但是,似乎没有多少口译员遵守这条规则。 Kangax 说他们应该被视为每个this page 的语法错误:
由于这些限制, 每当函数出现在一个块中 (如前面的例子)它 实际上应该被认为是一种语法 错误,不是函数声明或 表达。问题是几乎 我没有见过的实现 严格按照 规则(例外是 BESEN 和 DMD 脚本)。他们将它们解释为 取而代之的是专有方式。
【讨论】:
【参考方案6】:在 ECMAScript 标准中,FunctionDeclaration 未定义为 Statement,Statement 未定义为能够包含 FunctionDeclaration,因此根据标准它们是不兼容的(尽管实际上每个 JavaScript 解释器都会尝试做一些有意义的事情,尽管实现之间不一致)。
【讨论】:
【参考方案7】:是的,它是有效的。
所有语句块(即大括号内的所有内容)都可以有额外的语句和声明,包括函数。
所以你也可以在函数中定义函数等等
这里是 ECMA-262 v1 - http://www.mozilla.org/js/language/E262.pdf
【讨论】:
一个块可能包含零个或多个语句的列表。但函数声明不是语句。 你是对的,函数声明在块中是允许的。问题是一个浏览器将它们视为一个语句。 @RobG 哪个浏览器? @Taurus——实际上不止一个:在 Firefox、Chrome 和 Opera 中if (false) function foo()console.log('foo'); foo()
抛出类型错误:foo 不是函数。 foo 由于表达式被视为函数声明而被创建为变量,但由于它位于未执行的条件块中而未分配值(由于if (false)
) ,所以它被视为一个声明。在 Safari 中,它显示“foo”,因此它被视为函数声明并且赋值不是有条件的。
@RobG +1。但是,您在第一条评论中是在谈论 Firefox 吗?我之所以问,是因为我读过的许多文章都说只有 Firefox 将它们视为一种声明(我认为即使是 ECMA 规范也这么说),这些文章是否已经过时了?以上是关于函数声明可以出现在 JavaScript 的语句中吗?的主要内容,如果未能解决你的问题,请参考以下文章