1

I'm learning Javascript recently.When I get into the scope chain section, the example code as following:

var message = "in global";
console.log("global: message = " + message);
var a = function() {
  var message = "inside a";
  console.log("a: message = " + message);

  function b() {
    console.log("b: message = " + message);
  }
  b();
}

a();
Because of curiosity, I reverse the order of line 4 and line 5, as following:

var message = "in global";
console.log("global: message = " + message);
var a = function() {
  console.log("a: message = " + message); /*order reverse*/
  var message = "inside a"; /*order reverse*/
 
  function b() {
    console.log("b: message = " + message);
  }
  b();
}

a();

I suppose the second line of output should be a: message = in global instead of a: message = undefined. So why does this happen?

2
  • 2
    This is because regardless of where you define your variable, it will hoist the variable to the top of their enclosing scope. Which means, if a variable is defined in a scope, javascript moves it all the way at the top of the scope. This is the same reason you can call a function in javascript on line 1 even though the function doesn’t get defined until line 2. As a result in second example, you lose access to the globalVariable defined outside the scope of the function, because it has been hoisted to the top of the scope (aka inside the function). Commented Oct 28, 2020 at 10:01
  • w3schools.com/js/js_hoisting.asp Commented Oct 28, 2020 at 10:06

2 Answers 2

2

It is because Javascript will move all the variable declaration on the top of the block. It means that your code is equivalent to this:

var message = "in global";
console.log("global: message = " + message);
var a = function() {
  var message;
  console.log("a: message = " + message); /*order reverse*/
  message = "inside a"; /*order reverse*/
 
  function b() {
    console.log("b: message = " + message);
  }
  b();
}

a();

This issue can be avoided by using let:

var message = "in global";
console.log("global: message = " + message);
var a = function() {
  console.log("a: message = " + message); /*order reverse*/
  let message = "inside a"; /*order reverse*/
 
  function b() {
    console.log("b: message = " + message);
  }
  b();
}

a();

As you can see let won't be hoisted to the top of the block, hence throwing a compiler runtime error.

Sign up to request clarification or add additional context in comments.

1 Comment

It's not really a compiler error -- if you have everything in your code except a(), it won't throw an error at all. You're fine to define your function with an uninitialized variable message, you just can't call that function without making it error.
0

When JavaScript compiles right before your code run, it basically makes 2 passes over your code.

First it goes through and looks at the "left hand" of the assignments, i.e. var message. All of these are hoisted, which means they are virtually moved to the top of their scope, in your case the function.

Then it goes through and executes your code, and this time looks at the "right hand" side of assignments, i.e. = "inside a".

Because of that, when it does your console.log, it already knows about the a you have created inside of the function. Since this shadows the global a variable, the current value is "undefined" since it has not been set to "inside a" yet by the time you are doing your console log.

It's naturally confusing, which is why shadowing is discouraged.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.