Mark As Completed Discussion

Good morning! Here's our prompt for today.

Understanding Variable Scoping in JavaScript

Let's dig into a fascinating JavaScript example to unravel the mystery of variable scoping.

The Code Snippet

Here's the code we'll be examining:

JAVASCRIPT
1var numOfBeers = 5;
2
3function getMoreBeers() {
4  console.log('I have this many beers: ' + numOfBeers);
5  var numOfBeers = 25;
6  return numOfBeers;
7}
8
9console.log('I now have this many beers: ' + getMoreBeers());

What Do You Expect?

Before running the code, what do you think will be printed to the console? Take a moment to think it over.

Execution Results

Go ahead and execute the code. What do you see?

Your job in the interview is to explain why it runs the way it does.

Try to solve this here or in Interactive Mode.

How do I practice this challenge?

PYTHON
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

We'll now take you through what you need to know.

How do I use this guide?

Analyzing Output in JavaScript: The Case of Mysterious Beers

The Code and Its Output

Let's look at the JavaScript code snippet and its output when executed:

JAVASCRIPT
1var numOfBeers = 5;
2
3function getMoreBeers() {
4  console.log('I have this many beers: ' + numOfBeers);
5
6  var numOfBeers = 25;
7  return numOfBeers;
8}
9
10console.log('I now have this many beers: ' + getMoreBeers());
11
12// Output
13// I have this many beers: undefined
14// I now have this many beers: 25

Your Initial Guess vs Reality

Upon a first look, you might expect the output to show the number of beers as 5 and then 25. However, the output is quite different. Let's explore why.

JAVASCRIPT
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

Exploring Call Stack and Variable Hoisting: JS Execution

Console Log Order: The Call Stack in Action

The first thing to notice is the order in which the console log statements appear. The statement "I have this many beers" within the getMoreBeers function is printed before "I now have this many beers."

JAVASCRIPT
1console.log('I have this many beers: ' + numOfBeers);

Why does this happen? It's all thanks to the call stack, a data structure that keeps track of function calls. The call stack operates based on a stack data structure, which follows a First-In-Last-Out (FILO) principle.

When this line of code runs:

JAVASCRIPT
1console.log('I now have this many beers: ' + getMoreBeers());

It becomes the first frame on the call stack.

Function Calls and the Call Stack

However, as soon as getMoreBeers() is invoked, it gets pushed onto the top of the call stack, superseding the original console.log statement.

JAVASCRIPT
1function getMoreBeers() {
2  console.log('I have this many beers: ' + numOfBeers);
3  var numOfBeers = 25;
4  return numOfBeers;
5}

Only after getMoreBeers() fully executes, including its log statement, does the original console.log statement get popped off the stack and executed.

JS Execution

Hoisting: The Underlying Phenomenon

Now, let's delve into hoisting, which explains the value undefined in the output. In JavaScript, variable declarations are "hoisted" to the top of their respective scopes but remain uninitialized. This is why the numOfBeers inside getMoreBeers() is undefined during the first console.log.

JAVASCRIPT
1console.log('I have this many beers: ' + numOfBeers); // Output: undefined

Understanding the mechanics of the call stack and variable hoisting is crucial for both debugging and technical interviews.

Let's test your knowledge. Click the correct answer from the options.

What is a call stack?

Click the option that best answers the question.

  • A data structure for telephone calls
  • A stack that tracks method calls in a program
  • An ordering mechanism
  • A log of all the functions called

The Output Mystery

You've noticed that the output is intriguing. Despite having the variable numOfBeers defined before the function, the console log within the function prints it as undefined.

JAVASCRIPT
1// Output
2// I have this many beers: undefined
3// I now have this many beers: 25

The Role of Scoping and Hoisting

This oddity has to do with two core JavaScript concepts: scoping and hoisting.

  1. Local vs Global Scope: Even though a variable with the same name exists in the global scope, JavaScript gives priority to the local scope within a function.

    JAVASCRIPT
    1var numOfBeers = 5;  // Global Scope
  2. Hoisting in Action: In JavaScript, all variable declarations are "hoisted" to the top of their scope. This means that as soon as the function getMoreBeers() begins execution, JavaScript hoists the variable numOfBeers to the top of the function, but it remains uninitialized.

    JAVASCRIPT
    1function getMoreBeers() {
    2  console.log('I have this many beers: ' + numOfBeers);  // Local Scope, Hoisted but uninitialized
    3  var numOfBeers = 25;  // Local Scope, Initialized
    4  return numOfBeers;
    5}
  3. The Undefined Log: When the console.log statement within getMoreBeers() runs, it finds the local, hoisted, but uninitialized numOfBeers and prints it as undefined.

    JAVASCRIPT
    1console.log('I have this many beers: ' + numOfBeers);  // Output: undefined

Global Variable: A Common Misconception

Note that var numOfBeers = 5; is a globally defined variable. Intuitively, one might think that this global variable should be accessible within the getMoreBeers function.

JAVASCRIPT
1var numOfBeers = 5;  // Globally Defined Variable

Hoisting: The Culprit

However, this expectation is thwarted by hoisting. Recall that hoisting moves variable declarations to the top of their current scope during the compilation phase.

Global Variable

var vs const and let

The hoisting behavior is specific to the var keyword, making it distinct from const and let. During the lexical analysis step by JavaScript engines, the var numOfBeers inside getMoreBeers is hoisted to the top of the function's scope, even before the function executes.

JAVASCRIPT
1function getMoreBeers() {
2  // Hoisted but uninitialized
3  var numOfBeers;
4  console.log('I have this many beers: ' + numOfBeers);  // Output: undefined
5  numOfBeers = 25;  // Initialization
6  return numOfBeers;
7}

Breaking it Down: What Really Happens

So, when getMoreBeers runs, JavaScript's hoisting places a new var numOfBeers; at the top of the function, without initializing it. This local variable shadows the global one, and since it's uninitialized, the log statement shows undefined.

JAVASCRIPT
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

However, JavaScript only hoists declarations, not initializations. So the = 25 part is NOT hoisted alongside, thus numOfBeers is undefined when console.log('I have this many beers: ' + numOfBeers); is first run.

Complexity Of Final Solution

Complexity of Final Solution

O(1) constant time & space complexity, this is a knowledge question!

One Pager Cheat Sheet

  • We can observe that the getMoreBeers() function overwrites the numOfBeers variable, therefore when it is logged 25 will be printed out, which can be explained by scope.
  • Executing this code will print the output.
  • The order of the console log statements is determined by how the call stack works, with the getMoreBeers() function pushing the original statement onto the stack and executing first.
  • A call stack is a data structure that allows a program to keep track of function calls using the stack (first in, last out) data structure.
  • The variable is returned as "undefined" in the log despite being clearly defined above the function.
  • We should let/const and keep track of variable declarations to avoid unexpected results due to hoisting.
  • The declarations of variables with the var keyword in JavaScript are hoisted, but not the initializations.

This is our final solution.

To visualize the solution and step through the below code, click Visualize the Solution on the right-side menu or the VISUALIZE button in Interactive Mode.

PYTHON
OUTPUT
:001 > Cmd/Ctrl-Enter to run, Cmd/Ctrl-/ to comment

You're doing a wonderful job. Keep going!

If you had any problems with this tutorial, check out the main forum thread here.