函数声明可以出现在 JavaScript 的语句中吗?

IT技术 javascript
2021-03-03 07:32:20

请考虑将官方 ECMAScript 规范作为您的答案的来源,而不是由特定浏览器供应商发布的文档。(我知道 Mozilla 使用“函数语句”扩展其 JavaScript 实现。)

那么,根据 ECMAScript 规范,ergo,其中定义的句法产生式,这是有效的吗?

if (foo) {
    function x() { return; }
}

更新:我的问题也可以这样表述: Statement生成式是否包含FunctionDeclaration生成式?

结论:答案是否定的。

6个回答

我不同意其他说它有效的答案。

根据ECMA-262 第 5 版规范Blocks只能包含Statements第 12.1 节):

Block :
   { StatementList opt }

StatementList :
   Statement
   StatementList  Statement

然而,规范没有定义函数语句,而只定义了 aFunctionDeclaration和 a FunctionExpression规范进一步在第 12 节中对此进行了说明

众所周知,ECMAScript 的几个广泛使用的实现支持使用FunctionDeclaration作为Statement. 然而,在应用于此类的语义的实现之间存在显着且不可调和的变化FunctionDeclarations由于这些不可调和的差异,使用 aFunctionDeclaration作为Statement结果会导致代码在实现之间不能可靠地移植。建议 ECMAScript 实现要么禁止这种用法,FunctionDeclaration要么在遇到这种用法时发出警告。ECMAScript 的未来版本可能会定义替代的可移植方式来在Statement上下文中声明函数

如需进一步阅读,您可能还有兴趣查看comp.lang.javascript FAQ Section 4.2

4.2 什么是函数语句?

术语函数语句已被广泛且错误地用于描述FunctionDeclaration. 这是一种误导,因为在 ECMAScript 中,aFunctionDeclaration不是Statement; 程序中有些地方Statement允许 a 但不允许 a 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.

为了跨实现的一致行为,不要使用函数语句;使用FunctionExpressionFunctionDeclaration代替。

FunctionExpression 示例(有效):

var Fze;
try {
  Fze = function(b,a){return b.unselectable=a};
  /*...*/
} catch(e) { _DumpException(e) }

FunctionDeclaration 示例(有效):

// Program code
function aa(b,a){return b.unselectable=a}
+1 我完全同意。不应将函数声明放在块中,尤其是逻辑块或流程块(if、while、switch 等)。这也是 JSLint/JSHint 规则。
2021-04-18 07:32:20
惊人的答案,感谢您详细介绍,我一直在努力寻找合适的答案!
2021-04-20 07:32:20
请参阅ECMAScript 2015 §13.2,其中明确允许声明为StatementListItem函数声明在块中是允许的(并且一直都是)。唯一的问题是一个浏览器决定将块中的声明视为语句,这是允许的。
2021-04-26 07:32:20
自 2010 年 11 月(此答案发布的时间)以来已经过去了相当长的一段时间。我想知道从那时起规格是否发生了变化?
2021-05-14 07:32:20
很棒的答案,对 ECMAScript 规范的重要参考
2021-05-15 07:32:20

我不知道如何阅读这篇文章,但ECMA-262 V5有这样的说法:

注意 众所周知,ECMAScript 的几个广泛使用的实现支持FunctionDeclaration作为语句的使用然而,在应用于此类 FunctionDeclarations 的语义的实现之间存在显着且不可调和的变化。由于这些不可调和的差异,使用 FunctionDeclaration 作为 Statement 会导致代码在实现之间不能可靠地移植。建议 ECMAScript 实现要么禁止 FunctionDeclaration 的这种用法,要么在遇到这种用法时发出警告。ECMAScript 的未来版本可能会定义替代的可移植方式,用于在 Statement 上下文中声明函数。

如果我理解正确,严格来说,这意味着函数声明根本不能在块内,因为块只能包含语句。

不过,我的解释可能完全错误 - 我不熟悉 ECMAScript 的内部工作原理。

@Šime 谢谢!如前所述,我不是这里的专家,所以请谨慎对待。但@casablanca 所说的似乎证实了这一点
2021-05-01 07:32:20
哈利lua,有人懂生产规则
2021-05-09 07:32:20
这意味着函数声明根本不能在块内, ”不,它不是。这意味着块内的函数声明不应被视为语句。
2021-05-17 07:32:20

不,它是无效的。函数声明只能作为“源元素”出现,它们要么在全局范围内,要么直接在另一个函数定义中,在所有其他语句之外。来自 ECMA-262 规范:

函数体:源元素

程序:SourceElements

SourceElement : 声明 | 函数声明

语法中没有其他产生式允许FunctionDeclaration.

允许函数表达式作为语句的一部分:

MemberExpression : 函数表达式

...

语句:表达式语句

编辑:最近对另一个问题进行了相关讨论。请参阅对此答案的评论- 早些时候,我也认为这可能是有效的,但语法明确表明它是无效的。

来自 ECMA 262 第 14 章

  1. 程序语法

程序:SourceElements SourceElements:SourceElement SourceElements SourceElement SourceElement:Statement FunctionDeclaration Semantics

生产程序:SourceElements 的评估如下:

  1. 处理函数声明的 SourceElements。

  2. 评估 SourceElements。

  3. 返回结果(2)。

生产 SourceElements : SourceElement 为函数声明处理如下:

  1. 处理函数声明的 SourceElement。

生产 SourceElements : SourceElement 评估如下:

  1. 评估 SourceElement。

  2. 返回结果(1)。

生产 SourceElements : SourceElements SourceElement 为函数声明处理如下:

  1. 处理函数声明的 SourceElements。

  2. 处理函数声明的 SourceElement。

生产 SourceElements : SourceElements SourceElement 评估如下:

  1. 评估 SourceElements。

  2. 如果 Result(1) 是突然完成,则返回 Result(1)

  3. 评估 SourceElement。

  4. 返回结果(3)。

生产 SourceElement : *语句通过不采取任何行动来处理函数* 声明。

生产 SourceElement : *语句评估如下: *

1. 评估声明。

2. 返回结果(1)。

为函数声明处理生产 SourceElement : FunctionDeclaration 如下:

  1. Process FunctionDeclaration 用于函数声明(见第 13 节)。

生产 SourceElement : FunctionDeclaration 的评估如下:

  1. 返回(正常,空,空)。

awnser 正式为 NO。(Šime Vidas 在另一个问题中艰难地说服了我)

但是没有指定异常,因此它会失败或静默工作,具体取决于浏览器的实现。

ECMA-262 的第 5 版说它不应该是有效的:

FunctionDeclarations 只允许出现在 Program 或 FunctionBody 中。在语法上,它们不能出现在 Block ({ ... }) 中——例如 if、while 或 for 语句。这是因为 Blocks 只能包含 Statements,而不能包含 FunctionDeclaration 的 SourceElements。如果我们仔细查看产生式规则,我们可以看到在 Block 中允许 Expression 的唯一方式是当它是 ExpressionStatement 的一部分时。但是,ExpressionStatement 被明确定义为不以“function”关键字开头,这正是使 FunctionExpression 作为 Statement 或 Block 的一部分无效的原因(请注意,Block 只是 Statements 的列表)。

然而,似乎没有多少口译员遵守这个规则。Kangax 说他们应该被认为是这个页面的语法错误

由于这些限制,无论何时函数出现在块中(例如在前面的示例中),它实际上都应该被视为语法错误,而不是函数声明或表达式。问题是我见过的几乎没有一个实现严格按照规则解析这些函数(BESEN 和 DMDScript 除外)。相反,他们以专有的方式解释它们。