MDN给出如下示例,
// function declaration
function foo() {}
// function expression
(function bar() {})
// function expression
x = function hello() {}
if (x) {
// function expression
function world() {}
}
// function declaration
function a() {
// function declaration
function b() {}
if (0) {
// function expression
function c() {}
}
}
根据定义,表达式计算为一个值。
从上面的例子中,
1)函数world
是表达式还是声明?因为world
看起来像一个声明语句
2)函数c
是表达式还是声明?因为c
看起来像一个声明语句
3)我如何理解函数bar
在括号中的语法?var x = (function bar() {return 1})
和var x = function bar() {return 1}
有什么不同?
在MDN页面的评论是非常误导。(MDN是一个协作编辑的参考文献。通常都很好。)
从上面的例子中,
1)函数世界是表达式还是声明?
函数c是表达式还是声明?
直到ES2015(又名"ES6"),它们都是未指定行为,因为它们是控制流块中的函数声明。规范没有定义如何处理它们,但是在规范下处理它们是一个"允许的扩展",并且一些引擎确实支持它们。不幸的是,不同的引擎通过做不同的事情来支持它们。
但是在ES2015中,规范接受了它们:它们仍然是函数声明,但是如何解释取决于是否…
- …代码处于严格模式(行为是理性和直接的),
- …代码在浏览器托管的JavaScript引擎上处于松散模式,该引擎实现了规范附录B.3.3和附录B.3.4中描述的可选的遗留行为(令人困惑,只有几个场景是跨浏览器安全的),
- …或者代码是否在非浏览器JavaScript引擎上处于松散模式,理论上不应该实现Annex B(但这并不能阻止他们)。
因为在松散模式下你不能确定JavaScript引擎是要实现附件 B行为还是非附件 B行为,唯一合理的选择是:
- 使用严格模式,或者 不要使用块级函数声明。
如果您使用严格模式(无论是否在浏览器上),行为非常简单:声明被提升到其块的顶部,并且是块作用域(如let
和const
)。由声明创建的标识符是可写的,因此它是,就好像声明被转换为赋值给块顶部let
变量的函数表达式一样。让我们以world
为例,但添加一些。
:
"use strict";
// ...
function example() {
if (x) {
console.log("testing:");
console.log("1:", typeof world);
function world() {}
console.log("2:", typeof world);
}
console.log("3":, typeof world);
console.log("4:", world === undefined);
}
…有效地变成了:
"use strict";
// ...
function example() {
if (x) {
let world = function world() {};
console.log("testing:"); // testing:
console.log("1:", typeof world); // 1: function
console.log("2:", typeof world); // 2: function
}
console.log("3:", typeof world); // 3: undefined
console.log("4:", world === undefined); // ReferenceError: `world` is not defined
}
注意声明是如何被提升到块的顶部的,并且是块范围的。
在严格模式之外,这取决于,但非遗留版本就像严格模式,但使用var
而不是let
。
::
:
// NOT in strict mode
function example() {
if (x) {
console.log("testing:");
console.log("1:", typeof world);
function world() {}
console.log("2:", typeof world);
}
console.log("3":, typeof world);
console.log("4:", world === undefined);
}
…有效地变成了:
// NOT in strict mode
function example() {
var world;
if (x) {
world = function world() {};
console.log("testing:"); // testing: (if executed)
console.log("1:", typeof world); // 1: function (if executed)
console.log("2:", typeof world); // 2: function (if executed)
}
console.log("3:", typeof world); // 3: function if `x` is truthy, undefined if `x` is falsy
console.log("4:", world === undefined); // 4: false if `x` is truthy, true if `x` is falsy
}