11

This code works alerts "ok" in all browsers except Chrome:

eval("var outer = 0; function test() {'use strict'; outer = 1; } test(); alert('ok');");

(Try it on jsfiddle).

All I'm doing is referencing an outer variable from a 'use strict' function, all in eval context. Chrome says

Uncaught ReferenceError: outer is not defined 

Note: I originally faced it when using devtool: 'eval' in Webpack.

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
  • 1
    Your fiddle is set to use jsFiddle's *very surprising default* of wrapping all your code in a `window.onload` handler. (The second drop-down on the left.) If you turn that off, the code (at global scope) works as expected: http://jsfiddle.net/rokkkjcs/2/ I've also verified this without using jsFiddle (just in case jsFiddle is doing something weird). So that begs the question: Why should it matter whether the eval is happening within a function or at global scope? Either way, `test` should close over `outer`... V. v. weird. – T.J. Crowder Aug 21 '14 at 11:39
  • Oh, and did I mention that it works (even when wrapped in a function) when you run it directly from the console's REPL? Curiouser and curiouser... – T.J. Crowder Aug 21 '14 at 11:40
  • Using eval is not politically correct. – frenchie Aug 21 '14 at 21:06
  • 1
    @frenchie Did you read why I'm using it? – Dan Abramov Aug 22 '14 at 10:04

2 Answers2

3

To simplify the problem : http://jsfiddle.net/rokkkjcs/6/

eval("var outer=0;");
function test() {'use strict'; outer = 1; } 
test(); 
alert('ok');

And the explanation is :

Non-strict code can use the ‘eval’ function to add new variables to the surrounding scope. Prior to native JSON support in browsers, ‘eval’ was commonly (and unsafely) used to construct objects from strings. The constructed objects then became a part of the surrounding scope. In strict mode, ‘eval’ cannot introduce new variables. When executed in strict mode, the following piece of code will not introduce the ‘bar’ variable into the surrounding scope. Note: if a function containing ‘eval’ is executed in strict mode, then the code inside the ‘eval’ function is also executed in strict mode.

More info : http://cjihrig.com/blog/javascripts-strict-mode-and-why-you-should-use-it/

ovi
  • 566
  • 4
  • 17
  • I'm not quite sure I understand you here. Are you claiming your example is equivalent to mine? Can you explain why? – Dan Abramov Aug 21 '14 at 15:47
  • The only issue is adding a new variable to the surrounding scope with `eval()`. In strict mode, that variable will not be available. It works without a wrap because then it is in the global scope. – ovi Aug 21 '14 at 16:04
  • But the eval is not executed in strict mode, is it? – Bergi Aug 24 '14 at 12:24
1

Actually eval creates variables or modifies variables in scope where it is defined, no matter if you use val or not. Or in other words, by default it doesn't have it's own scope.

So when you do this

eval("var outer = 0;");
console.log(outer); //0

you create variable in outer scope. Surprisingly, this works the same way in chrome too - and it doesn't matter if window.onload is used or not.

To make eval has it's own scope you must do the following:

eval("'use strict'; var outer = 0;"); 
console.log(outer); //Uncaught ReferenceError: outer is not defined 

Now there is a separate scope for eval. With 'use strict' in eval your code will work in chrome and won't allow to override variables outside eval.

eval("'use strict'; var outer = 0; function test() {'use strict'; outer = 1; } test(); alert('ok');");

So this part answers how can you avoid error.

The second part that I am very intersted in but couldn't find an answer by myself.

The question is following, why your code throws an error in chrome while this works in chrome(which means the global variable is created):

window.onload = function() {
   eval("var outer = 0; function test(){console.log(outer); } test();");
}

And also why it happens only with window.onload.

Elena
  • 399
  • 2
  • 7