Slides

Scope

scope = all the variables that are visible from a given location in your code

including:

  • local variables (let and var)
  • function parameters
  • global variables
  • top-level functions

Global Scope

If you declare a variable without a keyword (var, let, const) then it is a global variable and can be seen and used by any line of code in your entire program

Global variables are very useful but also very dangerous. A mistake in any part of your program using a global variable could introduce a bug in any other part of your program using that global variable.

Implicit vs. Explicit globals

If you really want to use a global variable, you should do so explicitly, so other readers of your code will know that you did it intentionally.

JavaScript programs have a global object whose properties are available as global variables. In web browsers, the global object is named window; in NodeJS, the global object is named global.

// implicitly global
sendAnalytics = function(message) { ... }

// explicitly global
window.sendAnalytics = function(message) { ... }

Either of the above lines (in an HTML JS app) will allow any line in the entire rest of your program to call sendAnalytics('user clicked "unsubscribe" button')

Scope is a One-Way Mirror

scope is a one-way mirror -- inner scopes can see out, but outer scopes cannot see in

one way mirror functions

Variable Visibility

let name = 'Alice';         // this name is global
    function beta() {
      let name = 'Bob';     // this name is local to beta
      console.log(name);    // prints "Bob"
    }

console.log(name);          // prints "Alice"
    function alpha() {
      console.log(name);    // alpha can see global var
                            // prints "Alice"
      beta();               // alpha can see global function named beta
    }

alpha();

Another scope diagram

javascript scope diagram

What does this program print?

Scope Error

function gamma() {
    var x = "declared inside gamma";  // x can only be used in gamma
    console.log("Inside gamma");
    console.log(x);
}

console.log(x);  // Causes error

Closure Scope

JavaScript also supports lexical scope (aka "closure scope" or "nested scope") which means that variables defined above the current function may also be visible...

function sing() {               // outer function
  let numberOfBottles = 99

  function bottlesOfBeer() {    // inner function
    return '' + numberOfBottles 
      + ' bottles of beer on the wall'
  }

  while (numberOfBottles > 0) {
      console.log(bottlesOfBeer())
      numberOfBottles -= 1
  }

}

numberOfBottles is visible inside both sing() and bottlesOfBeer()

Lexical Scope

Closures add a layer between global and local:

  • local variables and parameters of nesting closures of the current function

This is called "lexical scope" because a given line of code can see all variables that are declared (= written = lexical) in the same code block, even if that code block is inside a different (nesting) function.

Nested Scopes

Every time you invoke a function, JS creates a new scope for that function

that points to the current scope

and so on recursively

Why Nested Scopes? 1

  • so callbacks can access local variables just like their neighboring code can

Why Nested Scopes? 2

  • nested functions, e.g.
function printGrid(grid) {

    function printRow(rowNum) {
        console.log(grid[rowNum].join(","));
    }

    let i = 0;
    while (i<grid.length) {
        printRow(i);
        i = i + 1;
    }
}

Why Nested Scopes? 3

  • higher-order functions (functions that use other functions, like map or forEach)
function countLetters(words) {
  let total = 0;
  words.forEach(function(word) {
    total += word.length;
  });
  return total;
}

total is visible inside the inner (callback) function as well as the outer (countLetters), so forEach can behave like other loops

Why Nested Scopes? 4

  • private state encapsulation with IIFE's (this is tricky; for more detail, see the encapsulation lesson)
let count = (function() {
    let value = 0;  // private variable
    let increment = function() {
        value = value + 1;
        console.log(value);
        return value;
    };
    return increment;
})();

count() // prints and returns 1
count() // prints and returns 2
value   // ReferenceError: value is not defined

Module Scope

  • NodeJS introduced the concept of module scope to the JavaScript world.
  • A variable defined at the "top" (left margin) of a file is visible to all other code in that file