zhouhy
zhouhy

Reputation: 312

Why does redeclaring a function identifier within a try block throw a SyntaxError?

The following lines of JavaScript

try {
    function _free() {}
    var _free = 1;
} finally { }

result in the following error:

 Uncaught SyntaxError: Identifier '_free' has already been declared

However, the following two blocks of JavaScript code don't:

  1. Without the try block scope:

    function _free() {}
    var _free = 1;
    
  2. Within a function scope:

    function a() {
        function _free() {}
        var _free = 1;
    }
    

But why?

(Testing environment: Chromium 61.0.3126.0)

Upvotes: 17

Views: 1437

Answers (2)

Leon Adler
Leon Adler

Reputation: 3331

To expand on Bergis answer, there is a difference in how the code is interpreted in ES5 and ES6 since block-scoped function declarations were added.

Input:

function test() {
    try {
        function _free() { }
        var _free = 1;
    } finally { }
}

Since ES5 does not support block-level functions, _free is hoisted to the parent function:

function test() {
    var _free = function _free() { }
    try {
        var _free = 1;
    } finally { }
}

In ES6, the function is declared at block-level, and semantically equal to a let/const declaration:

function test() {
    try {
        let _free = function _free() { }
        var _free = 1;
    } finally { }
}

This throws an error because var _free tries to declare a variable which is already declared. For example, this throws in ES6 as well:

let _free;
var _free = 1;    // SyntaxError: Indentifier '_free' has already been declared

While this is fine:

var _free;
var _free = 1;    // No SyntaxError

Setting the value of the already declared identifier is fine:

let _free;
_free = 1;

Therefore, to set the declared identifier _free to 1, you need to skip the second declaration:

try {
    function _free() { }
    _free = 1;
} finally { }

Upvotes: 5

Bergi
Bergi

Reputation: 664454

Because block-scoped function declarations are a new ES6 feature and were made safe (i.e. throw an error on name collisions, similar to let and const), but the other cases (which are programmer mistakes regardless) needed to stay backwards compatible and silently overwrite the function.

Upvotes: 15

Related Questions