1

As I understand it, Javascript doesn’t compile, it only runs. So there should be no compile-time errors, only runtime errors. So why doesn’t this code work?

function show() { console.log(x); }
(function () {
  var x = 42;
  show();
})()

My question isn’t on how to make this code better; I realize it is bad code and I already know how to fix it (see below).

My question is, why am I getting an Uncaught ReferenceError? If Javascript only throws errors at runtime, it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct?


working code:

(function () {
  var x = 42;
  function show() { console.log(x); }
  show();
})()

working code, best option:

function show(y) { console.log(y); }
(function () {
  var x = 42;
  show(x);
})()
chharvey
  • 8,580
  • 9
  • 56
  • 95
  • `x` doesn't exist in `show()`s scope, it is declared inside the anonymous function's scope. Yes, anonymous functions get their own scope. And to your related question - Javascript is an interpreted language and you do not compile it, but in modern JS engines, as the code is interpreted they do compile it to machine code. – skyline3000 May 07 '17 at 19:40

3 Answers3

1

Variable hoisting in JS is fun.

It's saying giving you the Reference Error because you've defined x inside of a closure (a function defined inside another function), meaning it's not available in the global scope and the show() method doesn't know it exists. If you defined it first, globally, it would of course work.

That said, scoping has been significantly improved in ES6+ with the use of let and const, so unless you're stuck with vanilla JS you'll probably find those to give a much more consistent and predictable coding experience.

This is a useful read on the subject: How do JavaScript closures work?

Community
  • 1
  • 1
LouisK
  • 1,126
  • 1
  • 8
  • 20
1

Note:Below description is as of ES5, NOT ES6 since in ES6 scoping rules have been changed(due to introduction of let).

Javascript does compile. It's just that like other languages such as c++/c# there is NO intermediate thing like exe/IL code that need to be clicked to start execution. In JS execution starts after compile phase.

So, when the compilation happens the compiler looks for function declaration and var declaration for variables. Therefore for this IIFE,

(function () {
  var x = 42;
  show();
})();

It does find one var declaration and the variable x is registered in the scope of the IIFE. This is called variable hoisting and x is available at the function level, in this case IIFE.

Later at execution time, the IIFE looks like this(conceptually):

(function () {
  //registration of x in this function's scope has already happened at 
  //compile time. Notice absence of `var`
  x = 42;
  show();
})();

Now, at this time the engine talks to scope and asks for lvalue reference of x. Since x was registered in IIFE, engine gets one and then 42 is assigned to it.

Now for this part:

function show() { console.log(x); }

When show is called, engine first asks scope of show function for x, since it doesn't have any variable registered with the name x, global scope is asked( rvalue reference) for x, since it was never registered with global scope either during compilation phase, it cannot be found in any of the scope and we get reference error.

it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct?

Because of scoping rules, variables in outer scopes are visible in inner scope but not vice-versa. x inside IIFE is visible at the IIFE level and not outside in outer scope.

Pankaj Shukla
  • 2,657
  • 2
  • 11
  • 18
  • Since the release of ES6, I know `let` is scoped to the block and not to the function, but how have scoping rules changed for `var`? I thought the behavior of `var` remains the same from ES5 to ES6. – chharvey May 07 '17 at 22:08
  • @chharvey I meant to say that function level scoping rules no longer apply to the variables due to let keyword(if declared using let). – Pankaj Shukla May 07 '17 at 22:11
  • @chharvey By the way the error you see is runtime error. Code did execute and threw the error. – Pankaj Shukla May 07 '17 at 22:30
1

show function gets scope where it's declared, not invoked.

Stanislav Mayorov
  • 4,298
  • 5
  • 21
  • 44