Sample Video Frame

28: Scope and Closures

In this exercise you will learn about the two complementary topics of scope and closure. Scope is the idea that variables declared at different nesting levels will not interfere with each other. A closure when you create a function while inside another function, it will carry with it the entire outer scope of where it was defined. These two concepts are very powerful when you start using them in the next exercise on partial application.

Var Sucks

Before we can talk about scope, we need to talk about var vs let. In the early days JavaScript had only two scopes where a variable was defined either globally or inside a function and that was it. This caused many bugs because the normal way scope works is inside every block structure, like an if-statement or while-loop. In this example I show you a simple function that declares a variable using each style, and then change it inside an if-statement to demonstrate the flaw.

const var_sucks = () => {
  var var_scope = 10;
  let let_scope = 20;

  console.log(`>>> var_sucks before if: var=${var_scope}, let=${let_scope}`);

  if(true) {
    var var_scope = 100;
    let let_scope = 100;
    console.log(`>>> var_sucks inside if: var=${var_scope}, let=${let_scope}`);
  }

  console.log(`>>> var_sucks after if: var=${var_scope}, let=${let_scope}`);
}

var_sucks();

When you run this code you see the flaw of var:

$ node "code.js" 
>>> var_sucks before if: var=10, let=20
>>> var_sucks inside if: var=100, let=100
>>> var_sucks after if: var=100, let=20

What you'll notice is that the let version works as expected and doesn't change the value of the outer variable. However, the var version changes the outer variable. If you are not aware of this then you will run into subtle bugs and have no idea what's going on, leading to complicated debugging sessions.

This is why I advocate simply using let for all of your variables, or const for anything that is a constant. It will avoid scoping bugs like you see here. I can also say that it's probably better to just not reuse variable names in your code, but many times you have to reuse them because you are using simple variable names in loops such as i.

Scope

The simplest way to understand scope is that any time you do {} then you can access variables from outside, but new variable declarations are fresh and only last until the closing }. You also get a new scope anytime you create a new function, and in new callbacks with anonymous functions. Finally, parameters to functions are also considered new and do not interfere with outer scopes.

You definitely need to watch the video for this concept as it is much easier to see how this works than it is to read about how it works. You should also play with the scope example so that you understand what's going on. Modify it and see if you can come up with different scenarios.

The Closure Code

When you create a function inside another function, it retains access to any variables that were created at that time in that scope. This would be any parameters, new variable declarations, or anything else that was around when you made the function. The easiest way to understand closures is to create a simple function that builds another function based on a setting. In this code you build something that simply adds two numbers.

// puzzle: how small can you make this?

const build_adder = (left) => {
  // do I really need this variable?
  let left_hand = left;
  return adder = (right) =>  {
    // do I really need the return?
    let result = left_hand + right;
    return result;
  }
}

let add10 = build_adder(10);
let add20 = build_adder(20);

console.log(`test builder 3 + 10 == ${add10(3)}`);
console.log(`test builder 3 + 20 == ${add20(3)}`);
console.log(`test builder 13 + 10 == ${add10(13)}`);
console.log(`test builder 3 + 10 + 20 == ${add10(add20(3))}`);

You'll notice that I can create two different adding functions and use them multiple times to add that fixed first number of 10 or 20. This is because the function that gets created inside build_adder remembers all of the variables it had access to at that time.

Again, you should watch the video for this exercise because it is much easier to see this work visually than it is to read a description about it.

What You Should See

When you run the closure code you should see this:

$ node "closure.js" 
test builder 3 + 10 == 13
test builder 3 + 20 == 23
test builder 13 + 10 == 23
test builder 3 + 10 + 20 == 33

Learn JavaScript Today

Register today for the Early Access course and get the all currently available videos and lessons, plus all future modules for no extra charge.


Still Not Sure? Try the next FREE Lesson!