curriculum/challenges/english/blocks/lecture-working-with-code-quality-and-execution-concepts/6732c6e281c14a61c4858361.md
Closures are one of the most powerful and often misunderstood features in JavaScript. At its core, a closure is a function that has access to variables in its outer enclosing lexical scope, even after the outer function has returned. This might sound complex but it's a fundamental concept that enables many advanced programming patterns in JavaScript.
To understand closures, let's start with an example:
:::interactive_editor
function outerFunction(x) {
let y = 10;
function innerFunction(){
console.log(x + y);
}
return innerFunction;
}
let closure = outerFunction(5);
console.log(closure()); // 15
:::
In this example, outerFunction takes a parameter x and defines a local variable y. It then defines an innerFunction that uses both x and y. Finally it returns innerFunction. When we call outerFunction(5) it returns innerFunction which we assign to the variable closure. When we later call closure() it still has access to x and y from outerFunction even though outerFunction has already finished executing. This is the essence of a closure.
The inner function maintains a reference to its outer lexical environment, preserving access to the variables in that environment even after the outer function has completed.
Closures are particularly useful for creating private variables and functions. Consider this example:
:::interactive_editor
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
let counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
:::
In this case, createCounter returns a function that increments and returns a count variable. The count variable is not directly accessible from outside createCounter, but the returned function (our closure) has access to it. Each time we call counter(), it increments and returns the count.
Closures can also capture multiple variables from their outer scope. For example:
:::interactive_editor
function multiply(x) {
return function (y) {
return x * y;
};
}
let double = multiply(2);
console.log(double(5)); // 10
:::
Here the inner function captures the x parameter from multiply. When we create double by calling multiply(2) it returns a function that always multiplies its argument by 2.
One important thing to note about closures is that they capture variables, by reference not by value. This means if the value of a captured variable changes, the closure will see the new value. For example:
:::interactive_editor
function createIncrementer() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
let increment = createIncrementer();
increment(); // 1
increment(); // 2
:::
Each time we call increment its working with the same count variable, not a copy of it's initial value. Closures are a powerful tool in JavaScript. as you continue to work with JavaScript you'll find that understanding and using closures effectively can greatly enhance your ability to write clean, efficient and powerful code.
What will be the output of the following code?
function outer(x) {
return function(y) {
return x + y;
};
}
let add5 = outer(5);
console.log(add5(3));
5
Consider how the inner function in outer captures the x parameter.
3
Consider how the inner function in outer captures the x parameter.
8
undefined
Consider how the inner function in outer captures the x parameter.
3
What concept does the following code demonstrate?
function createGreeter(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
}
let sayHello = createGreeter("Hello");
sayHello("Alice");
Hoisting.
Think about how the returned function retains access to the greeting parameter.
Closure.
Recursion.
Think about how the returned function retains access to the greeting parameter.
Prototype inheritance.
Think about how the returned function retains access to the greeting parameter.
2
What will be the output of the following code?
function counter() {
let count = 0;
return function() {
count++;
return count;
};
}
let increment = counter();
console.log(increment());
console.log(increment());
console.log(increment());
1
1
1
Consider how the inner function in counter maintains access to the count variable across multiple calls.
1
2
3
0
1
2
Consider how the inner function in counter maintains access to the count variable across multiple calls.
undefined
undefined
undefined
Consider how the inner function in counter maintains access to the count variable across multiple calls.
2