函数声明可以出现在 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 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 规范都这么说),这些文章是否已经过时了?【参考方案2】:

我不知道怎么读,但ECMA-262 V5 有这样的说法:

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

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

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

【讨论】:

哈利路亚,有人懂生产规则 @Šime 谢谢!如前所述,我不是这里的专家,所以持保留态度。但@casablanca 所说的似乎证实了这一点 "这意味着函数声明根本不能在块内," 不,它没有。这意味着块内的函数声明不应被视为语句。【参考方案3】:

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

根据ECMA-262 5th Edition specification,Blocks只能包含Statements(Section 12.1):

Block :
    StatementList opt 

StatementList :
   Statement
   StatementList  Statement

但是规范没有定义函数语句,而只定义了FunctionDeclarationFunctionExpression。该规范进一步在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

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

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 月(发布此答案的时间)以来已经过去了一段时间。我想知道从那以后规格是否发生了变化?【参考方案4】:

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

仅允许使用函数声明 出现在 Program 或 FunctionBody 中。 从语法上讲,它们不能出现在 块 ( ... ) — 例如 if 的块, while 或 for 语句。这是 因为块只能包含 语句,而不是 SourceElements,它 函数声明是。如果我们看 仔细制作规则,我们可以看到 唯一的表达方式是 Block 内允许的时间是 ExpressionStatement 的一部分。然而, ExpressionStatement 是明确的 定义为不以“功能”开头 关键字,这正是 使 FunctionExpression 无效为 语句或块的一部分(注意 该 Block 只是一个列表 声明)。

但是,似乎没有多少口译员遵守这条规则。 Kangax 说他们应该被视为每个this page 的语法错误:

由于这些限制, 每当函数出现在一个块中 (如前面的例子)它 实际上应该被认为是一种语法 错误,不是函数声明或 表达。问题是几乎 我没有见过的实现 严格按照 规则(例外是 BESEN 和 DMD 脚本)。他们将它们解释为 取而代之的是专有方式。

【讨论】:

【参考方案5】:

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

FunctionBody : 源元素

程序:源元素

SourceElement : 声明 |函数声明

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

只有函数表达式可以成为语句的一部分:

成员表达式:函数表达式

...

语句:表达式语句

编辑:最近有一个关于另一个问题的相关讨论。请参阅this answer 上的 cmets - 早些时候,我也认为这 可能 是有效的,但语法清楚地表明它是无效的。

【讨论】:

【参考方案6】:

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 在另一个问题上说服了我)

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

【讨论】:

【参考方案7】:

在 ECMAScript 标准中,FunctionDeclaration 未定义为 Statement,Statement 未定义为能够包含 FunctionDeclaration,因此根据标准它们是不兼容的(尽管实际上每个 JavaScript 解释器都会尝试做一些明智的事情,尽管实现之间不一致)。

【讨论】:

以上是关于函数声明可以出现在 JavaScript 的语句中吗?的主要内容,如果未能解决你的问题,请参考以下文章

深入理解javascript闭包

JavaScript中constvar和let区别浅析

前端修炼(第三天)函数

Javascript(声明,赋值)

JavaScript 变量声明提升

JavaScript 变量作用域 详解