A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain.
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
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
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.
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
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)
function someFunc() {
function zip() {
function foo() {
}
}
function quux() {
}
}
global
│
↓
someFunc()
│
╱ ╲
╱ ╲
↓ ↓
zip() quux()
│
↓
foo()
This code
makes this tree!
global
↑
│
someFunc()
↑
╱
╱
zip()
↑
│
foo()
Looking from the most inner to the most outer scope forms a Scope Chain:
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 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:
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.
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();
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)
}
}
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
$('.button').click(function() {
user.showFullName.call(user) // Chelsea Logan
})
$('.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.
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());
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.
console.log(test.call(obj.prop)) or console.log(test.apply(obj.prop))