What state is kept between JavaScript lines?

2019-04-07 10:25发布

问题:

I was wondering what states are kept between two lines of JavaScript code submitted to babel-node. My confusion arises because if you write two lines of code, you can override an variable definition without an error. For example, with babel-node --presets es2015, you can do:

> const a = 1;
undefined
> let a = 2;
undefined

Now if you write it in one line, you get an error:

> const a = 1; let a = 2;
TypeError: repl: Duplicate declaration "a"
...

It seems that in the first case, the state that a is defined as 1 (const variable assignment) is lost (yet not until the second assignment), while in the second case, it is maintained.

What causes the differences here? and which states are maintained?

回答1:

Because const and let are new syntaxes, they must be transpiled to the only binding mechanism that was available before ES6: var. In which case, var allows all sorts of haphazard reassignment without generating any kind of warning.

So when you type an expression in babel-node, babel transpiles it, evaluates it, then displays the result. Babel can check for misuse of a const binding at transpile time, which is why you're seeing the error for const a = 1; let a = 2. But const a = 1 and let a = 2, when transpiled/evaluated as separate expressions, will not exhibit the error because babel is not able to detect a problem in either expression alone.


A more visual demonstration of the issue: For every expression expr you type in the babel-node REPL, this is essentially what's happening

evaluate(transpile(expr))
// => someResult

So you won't see an error here

evaluate(transpile('const a = 1'))
evaluate('var a = 1')
// bind a to 1
// return undefined

evaluate(transpile('let a = 2'))
evaluate('var a = 2')
// bind a to 2
// return undefined

But you will see an error here

evaluate(transpile('const a = 1; let a = 2'))
// ERROR during transpile: const a has already been declared


回答2:

I do not use babel-repl, but it must be something to do with the conversion it is doing because everything works as expected with the regular REPL:

$ node -v
v7.4.0

$ node
> const a = 1;
undefined
> let a = 1;
SyntaxError: Identifier 'a' has already been declared

> const b = 1; let b = 1;
const b = 1; let b = 1;
                     ^
SyntaxError: Identifier 'b' has already been declared

> .editor
// Entering editor mode (^D to finish, ^C to cancel)
const c = 1;
let c = 1;

let c = 1;
        ^
SyntaxError: Identifier 'c' has already been declared