BY Jiancheng Zhang (prettyEcho@github)
Unless otherwise noted, all content on this page is shared under creative Commons attribution (CC BY 2.5AU)
Deep. Js, comments and star
The mood when writing this article is very uneasy 💢, because for our protagonist today: closure, many small partners have written about it, I believe we have also read a lot, those articles in the end there is no JS this approximate myth of things to speak clearly, to tell the truth, there really is, but few.
The original intention of writing this article: let all the partners who see this article have a thorough understanding of closures => improve JS level => can write higher quality JS code.
The reason why I said I was afraid of not reaching the original intention of writing this article, but I am confident that I will work hard to achieve my goal. If there is any misstatement in the article, we welcome you to correct it, thank you very much. 🙏 🙏 🙏
Here we go:
As many JS lovers have heard, closures are important but hard to understand.
I also felt that way at first, but after I tried to learn some deep principles of JS, I found closure is not so difficult to understand, but let me feel a very beautiful feeling. As soon as I understood the closure, I felt a sense of exhilaration, like when I was still drunk and still in flower.
Lift the veil of closure mystery
Let’s start with an example closure:
function foo() {
let a = 2;
function bar() {
console.log( a );
}
return bar;
}
let baz = foo();
baz();
Copy the code
Closure generated and Where is closure generated?
Let’s take our time:
You have to know what a closure is in order to figure out why it was created and where it is.
When a function can remember and access its lexical scope and scope chain, especially outside its defined scope, the function and its upper execution context together constitute a closure.
A few points to be clear:
- The closure must be a function object
- Closures are closely related to lexical scopes, scope chains, and garbage collection mechanisms
- Closures are generated when a function must be accessed outside its defined scope
- Closures are made up of both the function and its upper execution context (I’ll explain this in a bit more detail).
Now that we’re clear about what a closure is, let’s look at how closures come about.
Next, I assume that you have read my previous two articles about how JavaScript works inside JavaScript and how to understand JavaScript scope thoroughly. I recommend that you first read and understand the JS execution mechanism and scope, and then understand closures, otherwise you may not understand them thoroughly.
Now I’m assuming that the JS engine executes to this line of code
let bar = foo();
At this point, the JS scoped bubble looks like this:
At this point, foo should have finished executing, and JS’s garbage collection mechanism should automatically mark it as “out of the environment “, waiting for the next execution of the garbage collection mechanism to free its memory (flag cleanup).
However, if we look closely at the pink arrow in the figure, we point a reference to bar to Baz, and it is this reference assignment that prevents the garbage collection mechanism from collecting Foo, resulting in the entire scope chain of bar being preserved.
Next, baz() executes, bar enters the execution stack, closure (foo) is formed, and bar still has access to variable A in its parent scope bubble.
While this may not be very clear, let’s take a look at the closure generation process using Chrome’s debugging tools.
When the JS engine executes this line of code let bar = foo(); When:
As shown, let baz = foo(); Baz () is about to be executed. The Call Stack only has the global context.
The next baz (); Perform:
As you can see, the bar enters the Call Stack and Closure(foo) is formed.
Here are my explanations for the points I mentioned above:
- The second point (closures are related to lexical scope, scope chains, and garbage collection) should be clear
- Third, the closure is generated when the function baz is executed
- # 4, the closure is Foo, not bar. Many books (You Dont Know JavaScript, JavaScript Advanced Programming) emphasize the saved reference, i.e. bar in the above example is a closure, while Chrome considers the saved closed space Foo to be a closure. I agree with Chrome on this point (just for my own understanding, if you have different opinions, please feel free to discuss)
The art of closures
I believe that the most beautiful things in the world often exist around us, usually it is not so mysterious, so invisible, but we lack a pair of eyes to find beauty.
When we take time out to slow down and savor every moment of our lives, we reap another layer of joy from life.
Closures, too, are not mysterious, but rather ubiquitous in our programs, and when we take a moment to smell them, they exude an artistic beauty, simple, delicate and elegant.
Think about it. In our scoped bubble model, scoped chains allow our inner bar bubble to “see” the outside world, while closures allow our outer scope to “see” the inside. So, if we want to, the inner world and the outer world can be connected.
Considerations for using closures
Closures, in JS is absolutely a noble existence, it makes a lot of impossible code become possible, but things are good, but also reasonable use, otherwise not only can not achieve the effect we want, sometimes may also be counterproductive.
-
Memory Leak
JavaScript usually allocates less free memory to Web browsers than it does to desktop applications, mainly to prevent JavaScript Web pages from exhausting system memory and causing a system crash.
Therefore, the best way to make a page perform is to make sure that the page consumes the least amount of memory resources. That is, we should ensure that the executing code only stores the data that is useful. Once the data is no longer useful, we should let the garbage collection mechanism collect it and free the memory.
We now know that closures prevent garbage collection from collecting variables, so variables are always in memory, even when they are no longer in use, causing a memory leak that can seriously affect page performance. So when a variable object is no longer useful, we release it.
Let’s take the above code as an example:
function foo() { let a = 2; function bar() { console.log( a ); } return bar; } letbaz = foo(); baz(); // The object to which baz refers will always be in the heap. // If baz is no longer used, the object it points to is releasedCopy the code
Ruan Yifeng teacher’s blog is recommended for memory leaks.
Application of closures
-
The module
A module should have private properties, private methods and public properties, public methods.
Closures can expose the public properties and methods of modules.
var myModule = (function (window, undefined) { let name = "echo"; function getName() { return name; } return { name, getName } })(window); console.log( myModule.name ); // echo console.log( myModule.getName() ); // echo Copy the code
The “return” keyword applies to the closure by assigning the object reference export to myModule.
-
Time delay (setTimeout), counter (setInterval)
Here’s a quick example of a common interview question about closures.
for( var i = 0; i < 5; i++ ) { setTimeout(() => { console.log( i ); }, 1000 * i) } Copy the code
The answer is well known: output a 5 per second for a total of five times.
So how do you output a number every second, 0,1,2,3,4?
We are only going to cover closure solutions, we are not going to cover other solutions like block scope and so on.
for( var i = 0; i < 5; i++ ) { ((j) => { setTimeout(() => { console.log( j ); }, 1000 * j) })(i) } Copy the code
The “setTimeout” method uses closures that internally remember the lexical scope and scope chain of each loop.
Since the setTimeout callback executes at the end of the current task queue, the setTimeout callback in the first example remembers the value of I in the scope of the for loop, which is 5. The second example remembers the number of I as the value of j in the parent scope of setTimeout, which is 0,1,2,3,4.
-
The listener
var oDiv = document.querySeletor("#div"); oDiv.onclick = function() { console.log( oDiv.id ); } Copy the code
=- I think I made myself clear about closures, do you see that clearly? Leave a message and let me know -=
🤗 if you think the writing is not very bad, please follow minegithubCome on, let’s grow together…