Closures and This

Objectives

  • Understand and explain Javascript context.
  • Understand and explain closures.
  • Implement the module pattern in their code.

Review

  • Create a function.
  • Explain scope.
  • Manipulate the DOM with jQuery.

Introduction to Closures and Context

Closures

A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. 

Scope

A scope in JavaScript defines what variables you have access to. There are two kinds of scope – global scope and local scope.

var a = 1

function foo() {
  console.log(a)
}

foo() // 1

Global Scope

If you declare a variable outside of all functions it becomes globally scoped meaning it can be accessed from anywhere within the execution environment. In the example above, foo() logs the value for the global variable a.

function bar() {
  var b = 'local value'
  console.log(b)
}

bar() // 'local value'

console.log(b) // b is not defined

Local Scope

Variables declared inside of a function are given a local scope and can only be accessed from within the function they were declared. This is why when we run bar() our log of b gives us 'local value' but when we try to access boutside that scope we get b is not defined.

Scope Chains

The scoping rules work the same when there are functions nested inside of other functions; each function gets its own local scope, and variables in that scope can only be accessed from the lines of code within the function they were declared. This is called Lexical Scope.

function someFunc() {
  var outerVar = 1;
  function zip() {
    var innerVar = 2;
  }
}

zip has access to both innerVar & outerVar, but someFunc only has access to outerVar

 

 

Multiple Nested Scopes

Nesting isn't limited to a single inner scope, there can be multiple nested scopes, each of which adhere to the rule above. With one addition: sibling scopes are also restricted from accessing each other's variables.

function someFunc() {
  function zip() {
  }
  function quux() {
  }
}

zip & quux are both inner scopes of someFunc. Just as someFunc cannot access zip's variables, zip cannot access quux's variables (and vice versa)

Scope Tree

function someFunc() {
  function zip() {
    function foo() {
    }
  }
  function quux() {
  }
}
  global
       │
       ↓
   someFunc()
       │
      ╱ ╲
     ╱   ╲
    ↓     ↓
  zip() quux()
    │
    ↓
  foo()

This code

makes this tree!

Scope Chains

   global
       ↑
       │
   someFunc()
       ↑
      ╱
     ╱
  zip()
    ↑
    │
  foo()

Looking from the most inner to the most outer scope forms a Scope Chain:

Closures

    global
       ↑
       │
   someFunc()
    var bar
       ↑
       ⋮

Let's say someFunc() declares a variable bar:

 global
       ↑
       │
   someFunc()
    var bar
       ↑
      ╱
     ╱
   zip()
alert(bar)
    ↑
    ⋮

Given how nesting scope works, it's possible for an inner scope within someFunc() to access bar. In this example, let's say zip() accesses bar
 

Then zip() is said to Close Over bar. Therefore zip() is a Closure.

 global
       ↑
       │
   someFunc()
    var bar
       ↑
      ╱
     ╱
   zip()
alert(bar)
    ↑
    ⋮

The closure will be maintained even if zip() isn't executed immediately. It is perfectly legal in Javascript to pass ziparound / return it from someFunc() for later execution. All the while, bar will continue to be available.

This continues down the scope chain. Say zip() declares a variable beep, and foo() alerts it out:

<button>Click Me!</button>

document.addEventListener('DOMContentLoaded', function() {

  var count = 0

  document.querySelector('button').addEventListener('click', function() {
    alert(count)
  })
})

The inner function closes over the variable count, and continues to have access to that variable no matter how many times the user clicks on the <button>.

Note that because count is declared inside the outer function, it is not available in the global scope.

<button>Click Me!</button>

document.addEventListener('DOMContentLoaded', function() {

  var count = 0

  document.querySelector('button').addEventListener('click', function() {
    count = count + 1
    alert(count)
  })
})

We can then extend the code to increase the count each time the button is clicked:

 

<button id="increase">Increase Number +</button>
<button id="show">Show Me!</button>

document.addEventListener('DOMContentLoaded', function() {

  var count = 0

  document.querySelector('#increase').addEventListener('click', function() {
    count = count + 1
  })

  document.querySelector('#show').addEventListener('click', function() {
    alert(count)
  })
})

Because the variable count is in an outer function, and is closed over by the inner function, any modifications made to it anywhere along any of the scope chains leading to it are reflected in every closure:

Create a Closure

Independent Practice

Create a closure that will help you create colored sticky notes dynamically in your DOM with the click of a button.

The CSS is all setup for you in the start index.html, but you will have to add:

  • create input and button elements
  • run Javascript code only after document is "ready"
  • sticky note color and message should both be dictated by user input
  • each sticky note message should start with a number representing the order of its creation

Closure Brain Teaser

Independent Practice

function queueCreator(waitList){
  var positionInQueue = 1

  for (var i=0; i<waitList.length; i++) {
    waitList[i].id = function() {
      return positionInQueue
    }
    positionInQueue++
  }

  return waitList
}

var people = [{name:'George'},{name:'Chris'}]

var queueList = queueCreator(people)

queueList[0].id() // 3?!

As mentioned earlier, the ability for an inner function to reference an outer function variable can be dubious if that variable updates.

function queueCreator(waitList){
  var positionInQueue = 1

  for (var i=0; i<waitList.length; i++) {
    (function(position) {
      waitList[i].id = function() {
        return position
      }
    })(positionInQueue) // IIFE
    positionInQueue++
  }

  return waitList
}

var people = [{name:'George'},{name:'Chris'}]

var queueList = queueCreator(people)

queueList[0].id() // 1

Here is a potential solution using immediately invoked function expressions (IIFE):

function queueCreator(waitList){
  var positionInQueue = 1

  waitList.forEach(function(item, index) {
    item.id = function() {
      return positionInQueue + index
    }
  })

  return waitList
}

var people = [{name:'George'},{name:'Chris'}]

var queueList = queueCreator(people)

queueList[0].id() // 1

An alternative solution, which also helps explain how the above IIFE works, is to use any one of the built-in array iteration methods, such as forEach:

Here, the scope created by the function passed to .forEach also contains a unique value for index every time it is executed, allowing the closure created by .id to close over the single, unchanging value.

The Module Pattern 

var car;
function carFactory(kind) {
  var wheelCount, start;
  wheelCount = 4;
  start = function() {
    console.log('started the ' + wheelCount + ' wheel ' + kind + '.');
  };

  return {
    make: kind,
    wheels: wheelCount,
    startEngine: start
  };
}

car = carFactory('Tesla');

// => started the 4 wheel Tesla.
car.startEngine();

Module Pattern

Context

The Meaning and Purpose of this
console.log(this === window) // true

function foo() {
  console.log(this === window)
}

foo() // true

foo() === window.foo() // true

If we were to run this single line of code by itself, in the global variable scope, it would run in the context of the window object, and because this refers to the subject of the executing code, the subject in context, its value is equal to the window object it is running within. 

By default a function runs within the scope of the object it sits in, so in this case this is still equivalent to the window object.

var chatroomUser = {
  age: '22',
  sex: 'm',
  location: 'Los Angeles',
  printASL: function() {
    // we can refer to the chatroomUser object with this
    console.log(this.age + '/' + this.sex + '/' + this.location)

    // or we could refer to it by name
    console.log(chatroomUser.age + '/' + chatroomUser.sex + '/' + chatroomUser.location)
  }
}

Manipulating Context

var user = {
  firstName: 'Chelsea',
  lastName: 'Logan',
  showFullName: function() {
    console.log(this.firstName, this.lastName)
  }
}

user.showFullName() // Chelsea Logan

All objects, which includes functions, have properties. And when a function object executes, the value of this is set to the object that invokes said function.

var user = {
  firstName: 'Chelsea',
  lastName: 'Logan',
  showFullName: function() {
    console.log(this.firstName, this.lastName)
  }
}

$('.button').click(user.showFullName) // undefined undefined

Why would we want to change our context?

 

When it comes to the use of event handlers, the value of this is not always what we want

Here are the methods that allow us to control context

  • call: The call() method calls a function with a given this value and arguments provided individually
  • apply: The apply() method calls a function with a given this value and arguments provided as an array (or an array-like object)
  • bind: The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called

call:

$('.button').click(function() {
 user.showFullName.call(user) // Chelsea Logan
})

apply:

$('.button').click(function() {
 user.showFullName.apply(user) // Chelsea Logan
})
var user = {
  firstName: 'Chelsea',
  lastName: 'Logan',
  showFullName: function(one, two, three) {
    console.log(this.firstName, this.lastName, one, two, three)
  }
}

$('.button').click(function() {
 user.showFullName.call(user, 1, 2, 3) // Chelsea Logan 1 2 3
})

$('.button').click(function() {
 user.showFullName.apply(user, [1, 2, 3]) // Chelsea Logan 1 2 3
})
var user = {
  firstName: 'Chelsea',
  lastName: 'Logan',
  showFullName: function() {
    console.log(this.firstName, this.lastName)
  }
}

// declare a new variable whose value is the 
// user.showFullName function with a context set to user
var contextSetUser = user.showFullName.bind(user)
$('.button').click(contextSetUser) // Chelsea Logan

$('.button').click(user.showFullName) // undefined undefined

bind is different than call and apply in the sense that it doesn't set the context of a function, but rather bind creates a whole new function with the context you supply it.

This Brainteasers

var fullName = 'John Doe';
var obj = {
   fullName: 'Colin Ihrig',
   prop: {
      fullName: 'Aurelio De Rosa',
      getFullName: function() {
         return this.fullName;
      }
   }
};

console.log(obj.prop.getFullName());

var test = obj.prop.getFullName;

console.log(test());
  1. Explain the results of this code
  2. Make console.log(test())
    return Aurelio De Rosa

 

Brainteaser solution

  1. Context of a function is dependent on how a function is invoked, not how it's defined. For the first log, we execute obj.prop.getFullName() invoking .getFullName() upon the prop object, thereby setting the context of the function to prop and logging prop's fullName property, Aurelio De Rosa. For the second log, .getFullName()is set to the variable test which is in declared in the context of the window object, similar to the first example we saw earlier. Hence, when test is called, the log returns the value of the fullName property of the window object, John Doe.

  2. console.log(test.call(obj.prop)) or console.log(test.apply(obj.prop))

Conclusion

  • What is the purpose of the module pattern and what are its benefits?
  • When is context determined?
  • Why would you want to manually change context of a function?
  • What is a more commonly used to term for lexical scope?
  • Explain one ability closure has upon variable scope that makes it special.
  • How does closure work?
Made with Slides.com