Scope II: Scopes and Scope Chain

Goals

  • Describe the differences between var, let and const and when to use each
  • Predict how variables will behave when multiple scopes are involved
  • Understand how the scope chain is initialized and utilized to resolve variables

Vocab

  • Scope The level in which a variable can be accessed

Scope

Now that we understand the order of execution a bit, we can dive deeper into the concept of scope. Scope is the place in which a variable or value can be accessed.

Warmup

Variables often are described as having either global scope or local scope.

  • How would you describe the differences between a globally vs locally scoped variable?
  • Looking at the below example, does our makeNoise function have access to the cowNoise and catNoise variables?
  • What about outside of our function? Do we have access to cowNoise and catNoise here as well?
var cowNoise = 'moo';

function makeNoise() {
  var catNoise = 'meow';
  console.log('Cow Noise inside of Function: ', cowNoise);
  console.log('Cat Noise inside of Function: ', catNoise);
}

makeNoise();

console.log('Cow Noise outside of Function: ', cowNoise);
console.log('Cat Noise outside of Function: ', catNoise);

Global, Functional, and Block Scope

We have several scopes available to us: global, local (also known as function), block, and eval (the latter won’t be covered in this lesson - but you can read more on it here).

Global scope

In Your Notebook

Here is an example that uses globally scoped variables. What rules apply to them?

var one = "one";
let two = "two";
const three = "three";

showNumbers();

function showNumbers () {
  console.log("In function: ", one, two, three);
  if (one && two && three) {
    console.log("In if block: ", one, two, three);
  }
}

Global Scope Takeaways

  • Global scope is the default.
  • Everyone and everything has access to the global scope.
  • Functions and variables in the global scope are “vulnerable” because they can be accessed by everything and potentially mutated (changed).
  • var, let, and const can be globally scoped.

Function scope

In Your Notebook

Here is an example that uses locally scoped variables. What rules apply to them?

function readWords() {
  var greeting = "Hello, friend, ";
  let question = "how are you? ";
  const response = "I am fine."

  if (true) {
    console.log('Sentence in the if block: ', greeting, question, response);
  }

  console.log(greeting + question + response);
}

readWords();

// Am I able to access the variables here?
console.log(greeting + question + response);

Local Scope Takeaways

  • Variables declared in the function (using var, let, or const) can only be accessed by the other code inside the function.
  • You control what’s in the function scope, it cannot be meddled with by anyone or anything else.
  • The global scope cannot access function scope.

Block scope:

In Breakout Groups

Here is an example that uses block scoped variables. What rules apply to them?

function readWords() {
  var greeting = "Hello, friend, ";
  let question = "how are you? ";
  const response = "I am fine."

  if (true) {
     var greeting = "Sup dawg, ";
     let question = "what's good?";
     const response = "Nm."
     console.log('Sentence in if block: ', greeting, question, response);
  }

  console.log(greeting + question + response);
}

readWords();

Block Scope Takeaways

  • Variables declared in the block statement (if blocks, for loops, etc) using let or const can only be accessed by other code inside the block.
  • Variables declared in block statements using var will not be scoped within the block (as this is a special feature of let and const). Variables declared with var will “leak out”

Scope & Scope Execution Practice

Returning values

Let’s review how we can capture/store variables declared within a function and use them in the global scope:

Problem #1

Review the example below and answer the following questions:

function makeNumber () {
  var number = 5;

  return number;
};

console.log(number);
console.log(makeNumber);
console.log(makeNumber());
  • What do we get we logging the value of number?
  • What do we get we logging the value of makeNumber?
  • What do we get we logging the value of makeNumber()?
  • Bonus: How can we store the value returned from makeNumber() to use later?
Parent vs. Child Scopes

Let’s look at another example and compare how scopes work between the parent and child.

Problem #2

Review the example below and answer the following questions:

const array = [ 5, 4, 3, 2, 1 ];
const secondNumber = array[1];

function getFirstNumber () {
  const firstNumber = array[0];
  return firstNumber;
}

function getSecondNumber () {
  return secondNumber;
}

getFirstNumber();
getSecondNumber();

console.log(secondNumber);
console.log(firstNumber);
  • Run the getFirstNumber either in a Repl or your console. What happens & why?
  • Do the same for getSecondNumber and explain similarly what happens & why.
  • Finally, log secondNumber and firstNumber. Note what happens when doing one vs the other.

Review Note:

Parent scopes do not have access to child scopes BUT child scopes do have access to their parent scope.

Block Scoped Variable Practice

As we discussed earlier, variables declared with the keyword let or const will be block scoped if declared within a block. This means that they are scoped to the block statement (if, for…) in which they are declared. When you see { and }, those curly brackets are likely creating a scope, - as with function, if, and for.

Answer the following:

Run the following examples in your Repl/console:

// Example #1:
let message = 'You are doing great!';

if (message.length > 0) {
  let message = 'I think you are amazing!';

  console.log('Inside of conditional:', message);
}

console.log('Outside of conditional:', message);
// Example #2
function getIndex(searchQuery) {
  const names = ["Brittany", "Khalid", "Robbie"];

  for (let i = 0; i < names.length; i++) {
    if (names[i] === searchQuery) {
      console.log ('The index is: ', i);
      break;
    }
  }
  return i;
}

getIndex("Khalid"); // What will log and what will return?
  • Be prepared to explain what is happening and why. If you have time, replace let with var in Example #2 and note what happens!

In Your Notebook

  • Describe “scope” in your own words.
  • What are the similarities and differences between var, let, and const?
  • What might be a metaphor or analogy for scope? Draw or diagram it out.

Scope Chain

Whenever a variable is used, the JavaScript interpreter traverses the scope chain until it finds an entry for that variable. Traversal on the scope chain always starts in the most immediate (local) scope and moves towards the global space. Note that the scope chain is initialized during the “creation phase” of the interpreter running through the code. Let’s see an example of this in action!

In Breakout Groups

Consider the following example and explain what is happening:

let number = 10;

function foo () {
  number = 20;
  console.log('A', number);
}

console.log('B', number);

foo();

console.log('C', number);
  • Before running the code, what do you think the value of number is in each of the logs?
  • Now run it and take note of what happens. Allow each person in the group to explain what they think is happening.

What is happening here?

  1. foo and its definition as well as the declaration number are stored in global memory
  2. Line 1 - number is assigned the value of 10
  3. Line 8 - prints B 10 to the console
  4. Line 10 - foo is invoked, creating a new execution context
  5. Line 4 - A variable is declared without the keyword var and assigned a value. The interpreter searches in the current execution context to see where this variable was defined. Because it doesn’t find it declared in the current scope, it looks up the scope chain to the parent scope, which happens to be the global scope. The interpreter understands that this is to be treated as a re-assignment and assigned the value of number to 20, both locally and globally.
  6. Line 5 - prints A 20 to the console
  7. Line 12 - prints C 20 to the console

Major Takeaways

  • The scope chain (e.g. “What is the parent scope for this variable? The grandparent scope?”) is determined by where functions are defined in the code base…. not where functions are invoked.

  • Every time a variable is initialized, the interpreter will first look in its own scope to see if the label can be found. If it is not found, it will look “up” the scope chain to the parent scope to try to resolve the variable in the parent context.

  • If that label is never found, the interpreter will declare it globally on the window and the variable will be scoped as such.

Clarification between Scope Chain & Hoisting

It is important to note that the interpreter moving up the scope chain to resolve variable values is NOT hoisting. Remember that the JS interpreter hoists declarations (storing them in memory) during the creation phase, not when the code itself is being executed.

More Examples

With a partner, take turns walking through the following code examples:

Example 1:

function foo () {
  var localNumber = 20;
  number = localNumber;  
}

foo()

console.log(number);  // what will log here?

Example 2:

var givenName = 'Amon Williams';

function printGreeting() {
  console.log(`Hello ${givenName}`);
}

printGreeting('Khalid');  // what will log here?
printGreeting();      // what will log here?

Example 3:

var givenName = 'Amon Williams';

function printGreeting() {
  let givenName = "Khalid"
  console.log(`A: Hello ${givenName}`);

  if( givenName.split(" ").length < 2) {
    givenName = "Khalid Williams";
    console.log(`B: Hello ${givenName}`);
  }

  console.log(`C: Hello ${givenName}`);
}

printGreeting();

console.log(`D: Hello ${givenName}`);

// What logs at each letter?

Final Reflections

Using your journal, take a few minutes to answer the following:

  • Why is it important to understand scope?
  • What is the scope chain? What does it do?

Lesson Search Results

Showing top 10 results