块级函数

ES3 或更早版本中,在代码块中声明函数(即块级函数)严格来说应当是一个语法错误,但所有的浏览器却都支持该语法。可惜的是,每个支持该语法的浏览器都有轻微的行为差异,所以最佳实践就是不要在代码块中声明函数(更好的选择是使用函数表达式)。

为了控制这种不兼容行为,ES5 的严格模式为代码块内部的函数声明引入了一个错误,就像这样:

"use strict";

if (true) {

    // Throws a syntax error in ES5, not so in ES6
    function doSomething() {
        // ...
    }
}

ES5 中,这段代码会抛出语法错误。然而 ES6 会将 doSomething() 函数视为块级声明,并允许它在定义所在的代码块内部被访问。例如:

"use strict";

if (true) {

    console.log(typeof doSomething);        // "function"

    function doSomething() {
        // ...
    }

    doSomething();
}

console.log(typeof doSomething);            // "undefined"

块级函数会被提升到定义所在的代码块的顶部,因此 typeof doSomething 会返回 "function",即便该检查位于此函数定义位置之前。一旦 if 代码块执行完毕,doSomething() 也就不复存在。

决定何时使用块级函数

块级函数与 let 函数表达式相似,在执行流跳出定义所在的代码块之后,函数定义就会被移除。关键区别在于:块级函数会被提升到所在代码块的顶部;而使用 let 的函数表达式则不会,正如以下范例所示:

"use strict";

if (true) {

    console.log(typeof doSomething);        // throws error

    let doSomething = function () {
        // ...
    }

    doSomething();
}

console.log(typeof doSomething);

此处代码在 typeof doSomething 被执行时中断了,因为 let 声明尚未被执行,将 doSomething() 放入了暂时性死区。知道这个区别之后,你就可以根据是否想要提升来选择应当使用块级函数还是 let 表达式。

非严格模式的块级函数

ES6 在非严格模式下同样允许使用块级函数,但行为有细微不同。块级函数的作用域会被提升到所在函数或全局环境的顶部,而不是代码块的顶部。

// ECMAScript 6 behavior
if (true) {

    console.log(typeof doSomething);        // "function"

    function doSomething() {
        // ...
    }

    doSomething();
}

console.log(typeof doSomething);            // "function"

本例中的 doSomething() 会被提升到全局作用域,因此在 if 代码块外部它仍然存在。ES6 标准化了这种行为来移除浏览器之前存在的不兼容性,于是在所有 ES6 运行环境中其行为都会遵循相同的方式。

允许使用块级函数增强了在 JS 中声明函数的能力,但 ES6 还引入了一种全新的声明函数的方式。