Summary closure online article has rotten street, I dare not say how XXX this article, just a personal understanding of the summary. You just have a look, god still wants more corrections. Summary of this article and JavaScript Ninja Secrets, JavaScript You Didn’t Know, Volume 1
Series of blog: https://github.com/Nealyang/YOU-SHOULD-KNOW-JS
React-express-blog-demo amway React Stack + Express + Mongoose
preface
Why do we need to understand and master closures, not to mention ask if you want to be a JavaScript expert? Don’t? Are you going to have a job interview…
Again, for any front-end ER or JavaScript developer, understanding closures can be seen as a rebirth in another sense. Closures are a feature of pure functional programming languages, and because they greatly simplify complex operations, their use is easy to find in some JavaScript libraries and other high-level code.
In a word, closures, you have to master them.
Before we talk about closures, let’s talk about scopes
The scope we are talking about here is lexical scope. Lexical scope is the scope of the definition at the lexical stage. In other words, where you write variables and block scopes when you write code. Therefore, lexical parsing will remain scoped. (JavaScript engine runs JavaScript code through three steps: word segmentation/lexical analysis, parsing/syntax analysis and code generation).
Old rule: Look at the code.
function foo(a) {
var b = a*2;
function bar(c) {
console.log(a,b,c);
}
bar(b*3);
}
foo(2);/ / 2, 4 12
Copy the code
There are three nested scopes in this example, as shown below:
Screenshot from JavaScript You Don’t Know
Part one contains the entire scope, that is, the global scope. This contains the identifier: foo
Part two contains the scope created by Foo, which contains: A,bar, and b
Part three contains the scope created by BAR, which contains: c
The inclusion of these scoped bubbles gives the engine enough location information. In the above code, the engine executes console.log and looks for a, B, and C. It starts in the innermost scope, bar(…). Function scope. The engine cannot find variable A in this level of scope, so the engine goes up to nested scope foo(…). If found, then use.
If a and C both have scope bar(…) ,foo(…) Scope, console.log(…) That is, you don’t need to look up variables in foo’s outer scope.
No matter where functions are called, and no matter how they are called, their lexical scope is determined only by where functions are declared. Lexical scoping lookup only looks for first-level identifiers, such as A, B, and C.
For a brief introduction to the concept of lexical scope, also known as scope, check out JavaScript you Don’t Know for an introduction to cheating lexicality in JavaScript and more on lexical scope.
The concept of closures
When it comes to the concept of closures, this is really a bit vague, but let’s take a look at the concepts from various classic books
Concepts from the Definitive Guide to JavaScript
Function objects can be related to each other through scope chains, and variables within the body of a function can be stored in the scope of the function, a property known in computer science as closures
Concepts from the Definitive Guide to JavaScript
Closures are functions that have access to variables in the scope of another function.
Concepts from JavaScript Ninja Secrets
A closure is a scope created when a function is created to allow its own function to access and manipulate variables other than its own function.
Concepts from JavaScript you Don’t Know
Closures are a natural consequence of writing code based on lexical scope. Closures are created when a function remembers and accesses its lexical scope.
Personal understanding
A closure is a function that can access and manipulate variables inside other functions. Or a function defined inside a function. Because JavaScript doesn’t have dynamic scope, and closures are static scoped by nature (static scoping rules look for a variable declaration depending on the static relationship between blocks in the source program), functions access the scope we defined, the lexical scope. That’s why closures are implemented.
The common closure form is that function A covers function B, and then function A returns function B, so that function B can still access function A’s scope when executing outside of function A. The real power of closures comes from the fact that function B executes somewhere other than function A.
Substantive issues
function outer() {
var a = 2;
function inner() {
console.log(a);/ / 2
}
inner();
}
outer();
Copy the code
Based on lexical scope and lookup rules, the inner function can access the variable A defined inside Outer. Technically, this is a closure. But you can say no, because the reference methods used to explain inner’s reference to A are lookup rules for lexical scope, and these rules are only part of the closure.
Let’s modify the above code so that we can see the closure clearly
function outer() {
var a = 2;
function inner() {
console.log(a);
}
return inner;
}
var neal = outer();
neal();/ / 2
Copy the code
It’s probably because all the blogs on closures have used exhausted examples. Here the inner function is called normally and can access the variable A defined in outer. To be fair, after outer is run, the entire inner scope of the function is usually destroyed.
The magic of closures is that they prevent garbage collection from happening. In fact, the inner scope already exists and holds the A variable, so it is not collected. The inner function has a closure of the inner scope of the outer function, keeping the scope alive for the inner function to access at any time thereafter.
Inner () already holds a reference to this scope, and this reference is called a closure.
Functions are called outside the lexical scope at definition time, and closures allow functions to continue to access the lexical scope at definition time.
No matter how an inner function is passed outside its lexical scope, it holds a reference to the original definition scope and uses closures wherever the function is executed
var fn;
function foo() {
var a = 2;
function baz() {
console.log(a);
}
fn = baz;
}
function bar() {
fn();
}
foo();
bar();
Copy the code
The above code doesn’t need to be explained too much. It’s pretty simple, but let’s talk about three interesting concepts of closures
var outerValue = 'ninja';
var later;
function outFunction() {
var innerValue = 'Neal';
function innerFunction(param){
console.log(outerValue,innerValue,param,tooLate);
}
later = innerFunction;
}
console.log('tooLate is',tooLate);
outFunction();
later('Nealyang');
var tooLate = 'Haha';
later('Neal_yang');
//tooLate is undefined
//ninja Neal Nealyang undefined
//ninja Neal Neal_yang Haha
Copy the code
The above code results you can try. In summary, from the code above, we can see three interesting concepts for closures
- The arguments to the inner function are contained in the closure
- All variables outside the scope, even those declared after the function declaration, are included in the closure.
- Undeclared variables in the same scope cannot be referenced in advance
The code has closures everywhere
function wait(message) {
setTimeout( function timer() {
console.log( message ); }, 1000 ); }
wait( "Hello, closure!" );
Copy the code
As shown above, a very common timer, but the timer function has a closure that covers the wait scope because it also preserves a reference to the variable Message.
After a wait is executed for 1s, its internal scope does not disappear, and the timer function remains a wait-scoped closure.
Digging deeper into the engine’s internals, the built-in G utility function setTimeout holds a reference to a parameter, and the engine calls this function, in this case the internal timer function, while the lexical scope remains intact. That’s the closure.
Whenever and wherever you pass functions around as first-level value types, you’ll see closures used in those functions. Whenever you use callbacks in timers, event listeners, Ajax requests, cross-window communication, or other asynchronous tasks, you are using closures.
Use closures in the classic for loop
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i ); }, i*1000 ); }
Copy the code
In the for loop, we all know that the output is 6. After all, we only have one I in this scope, and all the callbacks are executed after the for loop ends.
If we try to assume that each iteration in the loop captures itself a copy of I at run time, the way scoping works is that although the five functions in the loop are defined separately in each iteration, they are all enclosed in a shared scope, so there is still only one I.
So back to business, we need to use closures, and each iteration in each loop will generate a closure scope.
So our code is modified as follows:
for (var i=1; i<=5; i++) { (function() {
setTimeout( function timer() {
console.log( i ); }, i*1000); }) (); }Copy the code
But!!!!!! As you can see, this is not the last name, isn’t IIFE producing a closure? Yes, but if the IIFE generated closure scope is nullable, then what’s the point of encapsulating it? So it needs something substantial for us to use.
for (var i=1; i<=5; i++) { (function(j) {
setTimeout( function timer() {
console.log( j ); }, j*1000 ); })( i ); }
Copy the code
Of course, the above problem can be solved by using ES6 let. But I won’t say too much about it here. You can Google it yourself.
The module
This part is easier to understand because closures are well block-level scoped and have good hiding of internal variables. So naturally we can use it as a tool for module development. Leave aside today’s export and import
I’ll just look at the example, because it’s a little bit more general
function foo() {
var something = "cool";
var another = [1.2.3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( ! "" ")); }return {
doSomething:doSomething,
doAnother:doAnother
}
}
Copy the code
For a brief explanation, the doSomething and doAnother functions have closures that cover the internal scope of module instances. When passing functions out of the lexical scope by returning an object containing a property reference, we have created closure conditions that can be observed and practiced.
- There must be an external enclosing function that must be called at least once
- A closed function must return at least one inner function so that the inner function can form a closure in the private scope and can access or modify the private state.
Of course, the above code could also be written in IIFE form. But there are so many good articles on closures out there that we’ll stop there.