Scope

If you took Intro to Javascript, you learned about _block _scope vs. _function _scope. These determine where a variable can be seen in some code. Computer scientists call this lexical scope.

However, there also exists _another _kind of scope called runtime scope. When a function is run, it creates a new runtime scope. This scope represents the _context _of the function, or more specifically, the set of variables available for the function to use.

So what exactly _does _a function have access to?

VIDEO

Scope

A function's runtime scope describes the variables available for use inside a given function. The code inside a function has access to:

  1. The function's arguments.
  2. Local variables declared within the function.
  3. Variables from its parent function's scope.
  4. Global variables.

Check out the following image that highlights a function's scope, then we'll take a look at a live example.

The nestedchild()function has access to alla,b, andcvariables. That is, these variables are in thechild()function's scope.

VIDEO

Here's the code from the preceding video.

Video Recap

In the previous video, theintroduceMyself()function contains a nestedintroduce()function. Whileintroduce()does not take in any arguments, nor are there any local variables declared within it -- variables in both the aforementioned settings are indeed inintroduce()'s scope.

introduce()_does _use the global variablemyName, however, as well as theyouvariable contained in its parent function,introduceMyself()(whereintroduce()was defined). Both are highlighted below:

const myName = 'Andrew';
// Global variable

function introduceMyself() {

  const you = 'student';
  // Variable declared where introduce() is defined
  // (i.e., within introduce()'s parent function, introduceMyself())

  function introduce() {
    console.log(`Hello, ${you}, I'm ${myName}!`);
  }

  return introduce();
}

QUESTION 1 OF 4

Consider the following:

const num1 = 5;

function functionOne() {
  const num2 = 10;

  function functionTwo(num3) {
    const num4 = 35;

    return num1 + num2 + num3 + num4;
  }

  return functionTwo(0);
}

Which variables doesfunctionTwo()have access to? Select all that apply:

  • num1

  • num2

  • num3

  • num4

  • It is indeterminate.

SUBMIT: All four variables are available forfunctionTwo()to use. Let's break this one down:

num1is a global variable, accessible anywhere in the application code.

num2is a local variable infunctionOne(), the parent function in whichfunctionTwo()is defined.

num3is an argument passed directly tofunctionTwo().

num4is a local variable infunctionTwo().

JavaScript is Function-Scoped

You may be wondering why scope is so heavily associated with_functions_in JavaScript. Especially if you've had past experience in another programming language, this might seem a bit unusual (e.g.,_blocks_in Ruby have their own scope)!

This is all because variables in JavaScript are traditionally defined in the scope of afunction, rather than in the scope of ablock. Since entering a function will change scope, any variables defined inside that function arenot_available outside of that function. On the other hand, if there are any variables defined inside a_block(e.g., within anifstatement), those variables_are_available outside of that block.

Let's see an example of how function-scoping in JavaScript works:

var globalNumber = 5;

function globalIncrementer() {
  const localNumber = 10;

  globalNumber += 1;
  return globalNumber;
}

In the example above,globalNumberis outside the function; it is a global variable that theglobalIncrementer()function has access to.globalIncrementer()simply has a local variable (localNumber) declared within it, then incrementsglobalNumberby 1 before returning the updated value ofglobalNumberitself.

After calling the function a few times, we see that the value ofglobalNumberhas indeed increased each time:

console.log(globalIncrementer());
// 6

console.log(globalIncrementer());
// 7

console.log(globalIncrementer());
// 8

However, when attempting to accesslocalNumberoutside of the function, we see a error:

console.log(localNumber);


// ReferenceError: localNumber is not defined

Because JavaScript is function-scoped, functions have access to all its own variables as well as all the global variables outside of it.

💡 Block-Scoping 💡

ES6 syntax allows for additional scope while declaring variables with theletandconstkeywords. These keywords are used to declare_block-scoped_variables in JavaScript, and largely replace the need forvar.

We've used them throughout this course, but for a closer look, check out our course:ES6 - JavaScript Improved. Via MDN:

Scope Chain

Whenever your code attempts to access a variable during a function call, the JavaScript interpreter will always start off by looking within its own local variables. If the variable isn't found, the search will continue looking up what is called thescope chain. Let's take a look at an example:

function one() {
  two();
  function two() {
    three();
    function three() {
      // function three's code here
    }
  }
}

one();

In the above example, whenone()is called, all the other nested functions will be called as well (all the way tothree()).

You can visualize the scope chain moving outwards starting at the innermost level: fromthree(), totwo(), toone(), and finally towindow(i.e., the global/window object). This way, the functionthree()will not only have access to the variables and functions "above" it (i.e., those oftwo()andone()) --three()will also have access to any global variables defined outsideone().

Let's now revisit the image from the beginning of this section, and visualize the entire process:

When resolving a variable, the JavaScript engine begins by looking at the nested child function's locally-defined variables. If found, then the value is retrieved; if not, the JavaScript engine continues to looking outward until the variable is resolved. If the JavaScript engine reaches the global scope and is still unable to resolve the variable, the variable is undefined.

💡 The Global (window) Object💡

Recall that when JavaScript applications run inside a host environment (e.g., a browser), the host provides awindowobject, otherwise known as theglobal object. Any global variables declared are accessed as_properties_of this object, which represents the outermost level of the scope chain.

For a refresher, feel free to check outBeware of Globalsin Lesson 1.

Variable Shadowing

What happens when you create a variable with the_same name_as another variable somewhere in the scope chain?

JavaScript won't throw an error or otherwise prevent you from creating that extra variable. In fact, the variable with local scope will just temporarily "shadow" the variable in the outer scope. This is calledvariable shadowing. Consider the following example:

const symbol = '¥';

function displayPrice(price) {
  const symbol = '$';
  console.log(symbol + price);
}

displayPrice('80');
// '$80'

In the above snippet, note thatsymbolis declared in two places:

  1. Outside the displayPrice() function, as a _global _variable.
  2. Inside the displayPrice() function, as a _local _variable.

After invokingdisplayPrice()and passing it an argument of'80', the function outputs'$80'to the console.

How does the JavaScript interpreter know which value ofsymbolto use? Well, since the variable pointing to'$'is declared inside a function (i.e., the "inner" scope), it will override any variables of the same name that belong in an outer scope -- such as the global variable pointing to'¥''. As a result,'$80'is displayed rather than'¥80'.

All in all, if there are any naming overlaps between variables in different contexts, they are all resolved by moving through the scope chain from inner to outer scopes (i.e., local all the way to global). This way, any local variables that have the same name take precedence over those with a wider scope.

QUESTION 2 OF 4

What will the console display whenmyFunction()is called?

let n = 2;

function myFunction() {
  let n = 8;
  console.log(n);
}

myFunction();
// ???
  • 2

  • 8

  • null

  • undefined

SUBMIT: When attempting to access a variable, the JavaScript engine look outwards, starting from the innermost level. In this example, since the JavaScript engine finds n declared as a local variable within myFuction() , it will never reach the n variable (whose value is 2) further up the chain. As such, 8 is logged to the console.

QUESTION 3 OF 4

When searching for variables along the scope chain, in what order will the JavaScript interpreter search?

ORDER LOCATION

1st: Local variables

2nd: Parent function's variables

3rd: Parent function's _parent function's _variables

4th: Global variables

SUBMIT: The JavaScript engine will look at the innermost level and move outward -- from local variables defined directly in a function, all the way up the the variables in the global scope (if needed).

QUESTION 4 OF 4

When the following code runs, what is the output of the first, second, and third logs to the console (respectively)?

let n = 8;

function functionOne() {
  let n = 9;

  function functionTwo() {
    let n = 10;
    console.log(n);  // First log
  }

  functionTwo();

  console.log(n);  // Second log
}

functionOne();

console.log(n);  // Third log
  • 8,9,10

  • 10,9,9

  • 9,10,8

  • 10,9,8

  • It is indeterminant.

SUBMIT: The code outputs10,9, and8to the console (in that particular order). With each log to the console, the JavaScript engine will search for the innermost (i.e., most local) variable it can find; this is the value passed into theconsole.log(n)expression.

WhenfunctionTwo()is called, even though there's annvariable that has a value of9, and anothernvariable that has a value of8-- the JavaScript engine won't find them! Since there's another variablenthat is more local (i.e., directly insidefunctionTwo()), the JavaScript engine will use_that_variable instead.

Summary

When a function is run, it creates its own scope. A function's scope is the set of variables available for use within that function. The scope of a function includes:

  1. The function's arguments.
  2. Local variables declared within the function.
  3. Variables from its parent function's scope.
  4. Global variables.

Variables in JavaScript are also function-scoped. This means that any variables defined inside a function are not available for use outside the function, though any variables defined within blocks (e.g.iforfor) _are _available outside that block.

When it comes to accessing variables, the JavaScript engine will traverse the scope chain, first looking at the innermost level (e.g., a function's local variables), then to outer scopes, eventually reaching the global scope if necessary.

In this section, we've seen quite a few examples of a nested function being able to access variables declared in its parent function's scope (i.e., in the scope in which that function was nested). These functions, combined with the lexical environment it which it was declared, actually have a very particular name: closure. Closures are _very _closely related to scope in JavaScript, and lead to some powerful and useful applications. We'll take a look at closures in detail next!

Further Research

results matching ""

    No results matching ""