8

I try to check whether a "variable" in es6 is constant:

const a = 1;
function test() {
  try {
    a = 2;// throws an error
  } catch (error) {
    console.log(error)
  }
}
test();

But when I use eval() function ,It doesn't throws an error.

const a = 1;
function test() {
  try {
    eval("a = 2;")// not throws an error
  } catch (error) {
    console.log(error)
  }
}
test();

And I make the constant local, the function eval does throw an error as expected.

function test() {
  try {
    const a = 1;
    eval("a = 2;")//throws an error
  } catch (error) {
    console.log(error)
  }
}
test();

And when I use function eval() that cause it doesn't throw an error as expectd. What the functoin eval() have done here ? I run it in Node v6.2.2

const a = 1;

function test() {
  try {
    eval("");
    a = 2; // will not throw an error
  } catch (e) {
    console.log(e)
  }
}
test();

More interesting, I enclose eval() with if (false),it will not throw an error either.

const a = 1;

function test() {
  try {
    if (false) {
      eval(""); 
    }
    a = 2; // will not throw an error
  } catch (e) {
    console.log(e)
  }
}
test();

Anyone can tell me the reason?

Is this a bug in JavaScript?

And how can I catch the global constant variable changed error?

zcmyworld
  • 393
  • 1
  • 2
  • 9

1 Answers1

4

The fact that eval("a = 2;") in your code doesn't throw a TypeError looks like a bug in V8 (the JavaScript engine in Chrome).

const creates a strict binding via the spec operation CreateImmutableBinding (see Step 16.b.i.1 of GlobalDeclarationInstantiation).

When assigning to an immutable binding (a = 2), a TypeError is meant to be thrown by SetMutableBinding if either 1. The code is in strict mode, or 2. The binding is a strict binding.

So in your code not using eval, you're running in loose mode, but it throws a TypeError anyway because the binding is strict. But V8 doesn't seem to be checking whether the binding is strict when you do eval("a = 2;"). (SpiderMonkey [Firefox] does, and throws the TypeError; as does JScript in IE11. I don't have Edge handy to check Chakra.)

You can make V8 throw the error by making the code in the eval strict:

const a = 1;
function test() {
  try {
    eval("'use strict'; a = 2;");
  }
  catch (error) {
    console.log(error);
  }
}
test();
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Also interesting that after `eval("a = 2")`, the value of `a` remains 1. – robertklep Mar 14 '17 at 11:12
  • 2
    @robertklep: That part's not surprising (boy would it be surprising if it were 2!), there are several non-strict read-only bindings defined in the spec, and this is how they behave in loose mode (writes are silently ignored). For instance, the name of a function in a named function expression (not declaration) is a read-only, non-strict binding within the function, so `(function foo() { foo = 42; console.log(typeof foo); })()` outputs `"function"` in loose mode (without throwing). – T.J. Crowder Mar 14 '17 at 12:19
  • Thanks for the clarification :) – robertklep Mar 14 '17 at 12:20
  • When I add function eval() before the line "a=2;", it doesn't throw any error either. – zcmyworld Mar 15 '17 at 03:51
  • @zcmyworld: Ah hah! That gives us a good idea where the bug is then. When there's an `eval` in a function's code, V8 basically can't optimize the code at all (since it can't be sure of the content of the `eval` and what it's going to do). So apparently when its first-stage compiler is outputting completely un-optimized code, it's failing to check the strict flag on the binding; but when outputting its standard partially-optimized code, it does. (V8 is a two-stage compiler, but we're just running our code once and wouldn't trigger the second stage. [It's getting an interpreter soon, too.]) – T.J. Crowder Mar 15 '17 at 06:43