preface
Before we learn about closures, we need to take a look at scope and lexical scope. Once you’ve mastered this, you can finally understand closures in one step.
scope
define
In JavaScript, the accessibility of variables is governed by scopes. A scope is created by a function or block of code. A variable can only be used within the function or code block in which it is defined. Out of range, it is inaccessible
In this casecount
Variables arefoo
The scope in which the function is created and accessible. If you access it from the outsidecount
An error is reported and the execution is complete under normal circumstancesfoo
After the function, the scope it owns is actively released and destroyed.
The variable separation
We can think of scope as a spatial policy in which each function has its own private scope, which isolates variables and controls their accessibility so that different scopes can have variables with the same name without conflict.
Respectively infoo()
andbar()
Scope definitioncount
Variables do not clash.
Scope nesting
We can nest innerFunc() in the outer function outerFunc(). The outerVar variable in the outer scope is accessible from the inner scope.
summary
- Scopes can be nested
- Variables in an outer scope can be accessed inside an inner scope
- If the external scope is not found, the global scope is eventually searched, from the inside to the outside layer of the search form
The scope chain
Extension: We call scope inheritance the act of an inner scope having access to an outer scope, while the outer scope does not have access to the inner scope. The global scope can be likened to a large house with many layers of small rooms (function scope or block scope), each with its own key 🔑. You can access the outside from the inside, but you can’t access the inside from the outside, and you can’t access each other
Lexical scope
JavaScript uses lexical scope. Lexical scope (static scope) is the scope defined at the lexical stage, It is called lexical (or static) because the engine (when lexicalized) determines the nested scope of the scope simply by looking at the JavaScript source code without executing the code, so the scope is left unchanged when the lexical analyzer processes the code.
const myGlobal = 0;
function func() {
const myVar = 1;
console.log(myGlobal); / / "0"
function innerOfFunc() {
const myInnerVar = 2;
console.log(myVar, myGlobal); 1 0 "/ /"
function innerOfInnerOfFunc() {
console.log(myInnerVar, myVar, myGlobal); 2 1 0 "/ /"
}
innerOfInnerOfFunc();
}
innerOfFunc();
}
func();
Copy the code
-
The lexical scope of innerOfInnerOffFunc() consists of innerOfUNC(), func(), and the global scope. Within innerOfInnerOffFunc(), you can access the variables myInnerVar, myVar, and myGlobal.
-
The lexical scope of innerFunc() consists of func() and the global scope. Within innerOfFunc(), you can access the variables myVar and myGlobal.
-
The lexical scope of func() contains only global scopes. In func(), you can access the variable myGlobal.
Description: Lexical scope means that the accessibility of a variable is determined by the position of the nested scoped variable in the source code, and that variables in the inner scope can be accessed in its outer scope. This scoped structure provides enough location information for the engine to follow a step-up lookup rule to find the corresponding variable.
A simple example:
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); / / 1
Copy the code
When bar() calls foo(), foo() does not look for the value variable in bar(). Instead, foo() looks for its own scope. If it cannot find it, foo() directly looks up the global scope according to the scope chain lookup mechanism. (Scoped lookup stops when the first matching identifier is found)
Extensions: eval() and with can “cheat” on lexical scopes, which can cause code to run slowly. Don’t use them. To be clear, javascript doesn’t actually have dynamic scopes
closure
You learned earlier that lexical scopes allow static access to variables in an external scope. One step away from closure!
Let’s review this example:
function outerFunc() {
let outerVar = 'I am outside! ';
function innerFunc() {
console.log(outerVar); // "I am outside!"
}
innerFunc();
}
outerFunc();
Copy the code
Now we know that the outerVar variable can be accessed from the lexical scope within the innerFunc() scope, where the innerFunc() call occurs within its lexical scope (outerFunc()’s scope).
Change innerFunc() to call outside its lexical scope (outerFunc()). Can innerFunc() still access outerVar?
Let’s adjust the code snippet:
function outerFunc() {
let outerVar = 'I am outside! ';
function innerFunc() {
console.log(outerVar); // "I am outside!"
}
return innerFunc;
}
const myInnerFunc = outerFunc();
myInnerFunc();
Copy the code
InnerFunc () is now executed outside of its lexical scope, but innerFunc() can still access outerVar from its lexical scope, even if executed outside of its lexical scope. That is, innerFunc() captures the (aka memory) variable outerVar from its lexical scope.
In other words, innerFunc() is a closure because it captures the variable outerVar in lexical scope.
Normally, whenouterFunc
After the function completes execution, its scope is destroyed and the garbage collector frees that memory space. Closures, on the other hand, are magicouterFunc
The scope of theinnerFunc
It still holds a reference to that scope, which is the closure.Here we have completed the final step in understanding closures:
Closure is a function, it from its place remember variables, form a private scope, to protect the private variables inside the without outside interference, in addition to protect private variables, you can also store some content, regardless of where it later, so no matter through which way to transfer the function of the internal to the lexical scope, It holds a reference to the original scope, and the closure is used no matter where the function is executed.
Rule of thumb for identifying closures: If you see a foreign variable in a function (not defined inside the function), then the function is most likely a closure because the foreign variable is caught.
You can see in the previous code snippet that the outer variable outerVar in the innerFunc() closure is captured from the outerFunc() scope.
To observe the value of the environment variable recorded in the closure, you can see it from the browser console. In the example above, if you use the browser console to add a breakpoint, you can see the following image:You can see that in the pictureinnerFunc
theScope (Scope)
There will be a name calledClosoure (closures)
This is the way you can watch the environment in a closure change its value.
summary
Closures give a function limited access to a defined lexical scope when it is called outside of its lexical scope.
Extension: In layman’s terms a closure is a function defined inside a function. The inner function always has access to the scope of the outer function (the small room can always have access to the big house).
The role of closures
- Access other function internal variables
- Protects variables from being reclaimed by the memory reclamation mechanism
- Avoiding contamination of global variables facilitates the encapsulation of local variables in the call context
Like the defineProperty reference to a DEP instance in VUe2, let’s continue with some examples of why closures are useful.
Closure example
Event handler
let countClicked = 0;
myButton.addEventListener('click'.function handleClick() {
countClicked++;
myText.innerText = `You clicked ${countClicked} times`;
});
Copy the code
This example text will update the number of clicks. When the button is clicked, handleClick() executes somewhere inside the DOM code, away from definition.
But as a closure, handleClick() catches countClicked from lexical scope and updates it when the click occurs. More importantly, myText is also captured.
The callback
Capturing variables from lexical scope is useful in callbacks.
function foo(message, time) {
setTimeout(function callback() {
console.log(message); // Hello, World!
}, 1000);
}
foo('Hello, World! '.1000);
Copy the code
Callback () is a closure because it captures the variable message.
A classic use scenario for closures
Another fairly classic closure scenario, which is often mentioned in interviews, is the function in the for loop.
var arr = [];
for(var i = 0; i< 5; i++){
arr[i] = function(){
return i;
}
}
arr[0] ();/ / 5
Copy the code
In this case, the I variable is shared. When the loop ends, I is already 5. So arr0 prints 5. To solve this problem, you need to remember the value of I when defining the anonymous function of arr[I], rather than letting it change all the time. The idea behind closures is to create a private scope for each iteration of I, and store the current value of I in this private scope. The I of each scope is independent and does not affect it, thus avoiding the problem of sharing I and I being 5 at the end. Here’s the code.
var arr = [];
for(var i = 0; i< 5; i++){
arr[i] = (function(i){
return function(){
return i;
}
})(i)
}
arr[0] ();/ / 0
Copy the code
The ultimate solution, this is ES6 knowledge, because previously in JS there is no concept of block level scope, in ES6 there is, Let declared variables can better solve the above problem.
var arr = [];
for (let i = 0; i < 5; i++) {
arr[i] = function () {
return i;
};
}
arr[0] ();/ / 0
Copy the code
Conclusion 🥇
Scope determines the accessibility of variables in JavaScript. It mainly includes function scope and block scope.
Lexical scope allows a function scope to statically access variables from an external scope.
Finally, closures are functions that capture variables from their lexical scope. In simple terms, a closure remembers variables from where it was defined, no matter where it was executed.
Closures capture variables in event handlers, callbacks. They are used for functional programming.
Extension 🏆
If you find this article helpful, check out my other articles at ❤️ :
👍 5 Design patterns to know for Web development 🍊
👍 10 simple tips to make your vue.js code more elegant 🍊
👍 classic interview questions! From entering urls to displaying pages, why don’t you start learning? 🍊
👍 The handshake process of the SSL protocol 🍊
👍 Unlock this, Apply, call, and bind new positions 🍊