Using 'let' as a variable name is not throwing any errors in google v8

I was writing some random code in the chrome developer console. For my surprise, chrome let me use let as a variable name which is completely wrong as let is a reserved keyword. I need to understand why is this happening.

Scenarios:

var const = 78 //throws an error as expected

var function = 46 //throws an error as expected

var let = 56 //didn't throw an error :O

let //prints 56, which is wrong because 'let' is a keyword

let ab = 90

ab //prints 90 as expected

This flaw exists in node. But, when I try it in Babel REPL it is throwing an error.

I think this is something to do with Google v8


Solution 1:

A nice write-up of the reasoning behind this can be found in this article by Mohsen Azimi. Here's a quick summary of it.

The following keywords are defined in the JavaScript spec as FutureReservedWord:

implements     interface   let       package    private
protected      public      static    yield

In normal mode, these can be used as variable names without errors; however, in strict mode they are treated as reserved words and will throw the following error:

SyntaxError: Cannot use the reserved word 'let' as a variable name in strict mode.

This is so that pre-ES2015 code doesn't break - if someone had named lots of their variables let in a legacy app, they probably wouldn't be happy if the JS spec suddenly broke everything.

Solution 2:

The usage of reserved ES6 keywords is forbidden only in strict mode for compatibility reasons.

Babel (via the strict mode plugin) use strict mode by default. In the browser or in Node you can implicitly set strict mode by adding "use strict"; in the beginning of the file or the function.

Running the following code snippet will throw an error in Chrome as you expect it:

"use strict";
var let = 43;
// Throws: Uncaught SyntaxError: Unexpected strict mode reserved word

Solution 3:

This is the joy of a growing language.

The short version is that const was listed in the ECMAScript 1st Edition as a "future reserved word" which meant that although it didn't have any meaning (then), it couldn't be used for identifiers. (And of course, function has always been a reserved word.) But let was neither a reserved word nor a future reserved word, so it could be used for identifiers (and was). It wasn't until the 5th edition that let was identified as a future reserved word, and then only in the new strict mode ES5 added. (Your example doesn't work in strict mode.) Since let wasn't reserved, it was potentially used in code in widespread use, and couldn't be retroactively made a purely reserved word in ES2015. So instead, it's still a valid identifier (in loose mode). The parser has to figure out whether it's a declaration or identifier by context. (This has the fun consequence that in loose mode, forgetting to type an identifier after let isn't a syntax error, let = 42; works just fine — even if let isn't declared anywhere [thanks to what I call The Horror of Implicit Globals]. These are good reasons to always use strict mode [because let can't be an identifier in strict mode, and strict mode doesn't have implicit globals].)

JavaScript also has contextual reserved words. async is a valid identifier (even in strict mode, and even inside an async function!), it only has special meaning in places where previously it would have been a syntax error for an identifier to be there:

// Since `blah function` here is a syntax error:
blah function foo() {
}
// ...no valid code would have an identifier in that position, so it was possible
// to add an `async` modifier:
async function foo() {
}

await is a valid identifier (even in strict mode), unless it's within an async function; then it's a reserved word. That's possible because async functions didn't exist before await, so there was no possibility of an async function existing that used await as an identifier. Similarly, yield is only a reserved word inside generator functions.