chharvey
chharvey

Reputation: 9336

Running a function with an undefined reference

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);
})()

Upvotes: 1

Views: 949

Answers (3)

Pankaj Shukla
Pankaj Shukla

Reputation: 2672

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.

Upvotes: 1

Stanislav Mayorov
Stanislav Mayorov

Reputation: 4452

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

Upvotes: 1

LouisK
LouisK

Reputation: 1176

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?

Upvotes: 1

Related Questions