为什么在try块中重新声明函数标识符会抛出SyntaxError?

zho*_*uhy 17 javascript syntax-error

以下几行JavaScript

try {
    function _free() {}
    var _free = 1;
} finally { }
Run Code Online (Sandbox Code Playgroud)

导致以下错误:

 Uncaught SyntaxError: Identifier '_free' has already been declared
Run Code Online (Sandbox Code Playgroud)

但是,以下两个JavaScript代码块不会:

  1. 没有try块范围:

    function _free() {}
    var _free = 1;
    
    Run Code Online (Sandbox Code Playgroud)
  2. function范围内:

    function a() {
        function _free() {}
        var _free = 1;
    }
    
    Run Code Online (Sandbox Code Playgroud)

但为什么?

(测试环境:Chromium 61.0.3126.0)

Ber*_*rgi 15

因为块范围的函数声明是新的ES6特性并且是安全的(即在名称冲突上抛出错误,类似于letconst),但其他情况(无论是程序员错误)需要保持向后兼容并静默覆盖函数.


Leo*_*ler 5

为了扩展Bergis的答案,自从添加了块范围的函数声明以来,在ES5和ES6中如何解释代码是有区别的.

输入:

function test() {
    try {
        function _free() { }
        var _free = 1;
    } finally { }
}
Run Code Online (Sandbox Code Playgroud)

由于ES5不支持块级函数,_free因此提升到父函数:

function test() {
    var _free = function _free() { }
    try {
        var _free = 1;
    } finally { }
}
Run Code Online (Sandbox Code Playgroud)

在ES6中,函数在块级声明,在语义上等于let/ constdeclaration:

function test() {
    try {
        let _free = function _free() { }
        var _free = 1;
    } finally { }
}
Run Code Online (Sandbox Code Playgroud)

这会引发错误,因为var _free尝试声明声明的变量.例如,这也引发了ES6:

let _free;
var _free = 1;    // SyntaxError: Indentifier '_free' has already been declared
Run Code Online (Sandbox Code Playgroud)

这很好:

var _free;
var _free = 1;    // No SyntaxError
Run Code Online (Sandbox Code Playgroud)

设置已声明的标识符的值是正常的:

let _free;
_free = 1;
Run Code Online (Sandbox Code Playgroud)

因此,要将声明的标识符设置_free为1,您需要跳过第二个声明:

try {
    function _free() { }
    _free = 1;
} finally { }
Run Code Online (Sandbox Code Playgroud)