Javascript closures in a nutshell
1. Introduction
Closure (closure) is a difficult point of the Javascript language. It is often asked in interviews, and it is also its feature. Many advanced applications rely on closures. This article uses simple and easy-to-understand words as much as possible to explain the concept of closure, its formation conditions and its common interview questions.
Let's look at an example first:
var n = 999;
function f1() {
console.log(n);
}
f1() // 999
In the above code, the function f1 can read the global variable n. However, variables declared inside the function cannot be read from outside the function.
function f1() {
var n = 999;
}
console.log(n)
// Uncaught ReferenceError: n is not defined
In the above code, the variable n declared inside the function f1 cannot be read outside the function.
If you sometimes need to get local variables inside a function. Normally, this is not possible and can only be achieved through workarounds. That is, inside the function, define another function.
function f1() {
var n = 999;
function f2() {
console.log(n); // 999
}
}
In the above code, the function f2 is inside the function f1, and all local variables inside f1 are visible to f2. Since f2 can read the local variables of f1, as long as f2 is used as the return value, can't we read its internal variables outside f1!
2. What is a closure
We can modify the above code as follows:
function f1(){
var a = 999;
function f2(){
console.log(a);
}
return f2; // f1 returns a reference to f2
}
var result = f1(); // result is the f2 function
result(); // Execute result, there is no definition of a in the global scope,
//But the function closure can remember the scope when the function was defined, and output 999
In the above code, the return value of the function f1 is the function f2. Since f2 can read the internal variables of f1, the internal variables of f1 can be obtained externally.
A closure is a function f2, a function that can read variables inside other functions. Since in the JavaScript language, only sub-functions inside functions can read internal variables, so closures can be simply understood as "functions defined inside a function". The biggest feature of the closure is that it can "remember" the environment where it was born. For example, f2 remembers the environment f1 where it was born, so the internal variables of f1 can be obtained from f2 . In essence, a closure is a bridge between the inside of the function and the outside of the function.
So what exactly is a closure?
Closures are created when a function can remember and access the lexical scope it is in, even if the function executes outside the current lexical scope. ---- "The Javascript You Don't Know"
My personal understanding is that a closure is a function within a function (other languages can’t set functions again) , the function inside can access the variables of the outside function, and the outside variables are part of the inside function.
Conditions for the formation of closures
- function nesting
- An inner function references a local variable of an outer function
Third, the characteristics of closures
Every function is a closure, and every function is inherently able to remember the scope environment in which it was defined . Move a function out of the scope in which it was defined, move it, and run it. This function can actually remember the scope when it was defined. Wherever the function goes, the scope at the time of definition takes it . Next we use two examples to illustrate this problem:
//Example 1
var inner;
function outer(){
var a=250;
inner=function(){
alert(a);//Although this function is executed outside, it can remember the scope when it was defined, a is 250
}
}
outer();
var a=300;
inner();//When a function is executed, it looks for the variables in the closure and ignores the current scope.
//Example 2
function outer(x){
function inner(y){
console.log(x+y);
}
return inner;
}
var inn=outer(3);//After the number 3 is passed to the outer function, x in the inner function will remember this value
inn(5);//When the inner function passes in 5 again, it will only assign a value to y, so 8 is finally popped up
Fourth, the memory leak of the closure
Stack memory provides an execution environment, that is, scope, including global scope and private scope, when do they release memory?
- Global scope - the global scope is destroyed only when the page is closed
- Private scope - only generated by function execution
Under normal circumstances, function execution will form a new private scope. When the code in the private scope is executed, our current scope will be actively released and destroyed. But when the function execution returns a value of reference data type, and it is received by something else outside the function, the private scope generally formed in this case will not be destroyed .
Such as the following situation:
function fn(){
var num=100;
return function(){
}
}
var f=fn();//The private scope formed by fn execution can no longer be destroyed
That is, like the above code, the private scope inside the fn function will be occupied all the time, and a memory leak occurs. A memory leak is any object that persists after you no longer have or need it. Closures cannot be abused, otherwise it will cause memory leaks and affect the performance of web pages. After the closure is used up, to release the resource immediately, point the reference variable to null .
Next, let's take a look at a classic interview question about memory leaks:
function outer(){
var num=0;//Internal variables
return function add(){//Return the add function by return, you can access it outside the outer function
num++;//The internal function has a reference as part of the add function
console.log(num);
};
}
var func1=outer();
func1();//Actually call the add function, output 1
func1();//Output 2 because the private scope inside the outer function will always be occupied
var func2=outer();
func2(); // output 1 Every time the function is rereferenced, the closure is brand new.
func2();// output 2
5. The role of closures
1. You can read the variables inside the function .
2. The value of the variable can be stored in the memory for a long time, and the life cycle is relatively long . Therefore, the closure cannot be abused, otherwise it will cause performance problems of the web page
3. Can be used to implement JS modules .
JS module: a js file with specific functions, encapsulates all data and functions inside a function (private), and only exposes an object or function that contains n methods. The user of the module only needs to pass The objects exposed by the module call methods to implement the corresponding functions .
Please see the example below:
//index.html
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
myModule2.doSomething()
myModule2.doOtherthing()
</script>
//myModule.js file
(function () {
var msg = 'Beijing'//private data
//function to manipulate data
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
// Expose the object to the outside (two methods for external use)
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()
Six, the use of closures
We want to implement such a requirement: click a button, and prompt "the nth button is clicked", here we don't need event proxy first:
.....
<button>Test 1</button>
<button>Test 2</button>
<button>Test 3</button>
<script type="text/javascript">
var btns = document.getElementsByTagName('button')
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
console.log('th' + (i + 1) + 'number')
}
}
</script>
Unexpectedly, when any button is clicked, the "fourth" pops up in the background. This is because i is a global variable. When the click event is executed, the value of i is 3 at this time. How to modify it, the easiest is to declare i with let
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
console.log('th' + (i + 1) + 'number')
}
}
In addition, we can modify it by means of closures:
for (var i = 0; i < btns.length; i++) {
(function (j) {
btns[j].onclick = function () {
console.log('th' + (j + 1) + 'th')
}
})(i)
}