Home  >  Article  >  Web Front-end  >  In-depth understanding of closures in js learning

In-depth understanding of closures in js learning

高洛峰
高洛峰Original
2016-12-06 11:21:16895browse

Closure is a relatively difficult point to understand in js, especially for people without programming foundation.

In fact, there are only a few things you should pay attention to when it comes to closures. If you understand them all, it is not difficult to conquer them. Let's talk about some basic principles of closures.

The concept of closure

A closure is a combination of a function and the scope object in the created function. (Scope objects will be discussed below)

To put it more simply, "As long as one or more functions are nested in a function, we can call them a closure."

Similar to this:

function A() {
 var i = 5;
 return function() {
  console.log('i = '+i);
 }
}
 
var a = A();
a(); // i = 5

Principle of closure

1. If the local variables of the external function are called by the closure function, they will not be recycled immediately after the external function is executed.

We know that no matter what language, the operating system will have a garbage collection mechanism to recycle excess allocated space to reduce memory. The life cycle of a function begins when it is called. When the function call is completed, the local variables inside the function will be recycled by the recycling mechanism.

Let’s take the above example as an example. When our external function A is called, the local variable i in A will be recycled by the operating system and cease to exist. However, when we use a closure, the result is not like that. i will not be recycled. Just imagine, if i is recycled, wouldn't the returned function print undefined?

i Why hasn’t it been recycled?

When JavaScript executes a function, a scope object is created, the local variables in the function (the formal parameters of the function are also local variables) are saved in it, and the variables passed into the function are initialized together.

So when A is called, a scope object is created, let’s call it Aa, then this Aa should be like this: Aa { i: 5; }; After A function returns a function, A is executed . The Aa object should have been recycled, but because the returned function uses the attribute i of Aa, the returned function saves a reference to Aa, so Aa will not be recycled.

So by understanding the scope object, you can understand why the local variables of the function will not be recycled immediately when the function call is completed when encountering a closure.

Another example:

function A(age) {
 var name = 'wind';
 var sayHello = function() {
  console.log('hello, '+name+', you are '+age+' years old!');
 };
 return sayHello;
}
var wind = A(20);
wind(); // hello, wind, you are 20 years old!

Can you tell what its scope object Ww is?

Ww{ age: 20; name: 'wind'; };

2. Every time an external function is called, a new closure is generated, and the previous closures still exist and do not affect each other.

3. The same closure will retain the last state, and when it is called again, it will be based on the last time.

The scope object generated each time the external function is called is different. You can think of it this way. In the above example, the parameter age you pass in is different each time, so the object generated is different each time.

Every time an external function is called, a new scope object will be generated.

function A() {
 var num = 42;
 return function() { console.log(num++); }
}
var a = A();
a(); // 42
a(); // 43
 
var b = A(); // 重新调用A(),形成新闭包
b(); // 42

This code allows us to discover two things. First, when we call a(); twice in a row, num will be incremented on the original basis. It means that the same closure will retain the last state, and when it is called again, it will be based on the last time. 2. The result of our b(); is 42, indicating that it is a new closure and is not affected by other closures.

We can think of it this way, just like we blow a soap bubble. Every time I blow it (call an external function), a new soap bubble (closure) will be generated. Multiple soap bubbles can exist at the same time and two soap bubbles can exist at the same time. Bubbles will not affect each other.

4. Multiple functions existing in external functions "live and die together"

The following three functions are declared at the same time and can access and operate the properties (local variables) of the scope object.

var fun1, fun2, fun3;
function A() {
 var num = 42;
 fun1 = function() { console.log(num); }
 fun2 = function() { num++; }
 fun3 = function() { num--; }
}
 
A();
fun1();  // 42
fun2();
fun2();
fun1();  // 44
fun3();
fun1();  //43
 
var old = fun1;
 
A();
fun1();  // 42
old();  // 43  上一个闭包的fun1()

Since functions cannot have multiple return values, I used global variables. Again we can see that a new closure is created the second time we call A().

When a closure encounters a loop variable

When we talk about closures, we have to talk about the situation when a closure encounters a loop variable. Look at the following code:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    var item = &#39;item&#39; + i;
    result.push( function() {console.log(item + &#39; &#39; + arr[i])} );
  }
  return result;
}
 
var fnlist = buildArr([1,2,3]);
fnlist[0](); // item2 undefined
fnlist[1](); // item2 undefined
fnlist[2](); // item2 undefined

How could this happen? The three outputs we envision should be item0 1, item1 2, item2 3. Why is there three item2 undefined stored in the returned result array?

It turns out that when a closure encounters a loop variable, the variable value is saved uniformly after the loop ends. Take our example above, i is a loop variable. When the loop ends, i happens to be 3 after i++, and arr [3] has no value, so it is undefined. Some people may wonder: Why is the value of item2? Shouldn't it be item3? Note that in the last loop, that is, when i = 2, the value of item is item2. When i++, i = 3, the loop conditions are not met and the loop ends. The value of item at this time has been determined, so at this time arr[i] is arr[3], and item is item2. Is this understandable? It makes sense if we change the code to this:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push( function() {console.log(&#39;item&#39; + i + &#39; &#39; + arr[i])} );
  }
  return result;
}
 
var fnlist = buildArr([1,2,3]);
fnlist[1](); // item3 undefined

Then the question is, how to correct it? Let’s look at the code:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push( (function(n) {
      return function() {
       var item = &#39;item&#39; + n;
       console.log(item + &#39; &#39; + arr[n]);
      }
    })(i));
  }
  return result;
}
 
var fnlist = buildArr([1,2,3]);
fnlist[0](); // item0 1
fnlist[1](); // item1 2
fnlist[2](); // item2 3

We can use a self-executing function to bind i, so that every state of i will be stored, and the answer will be the same as we expected.

So when we encounter loop variables when using closures in the future, we must habitually think of using a self-executing function to bind it.

The above is my understanding of closures. If you have any comments or suggestions, I hope we can communicate more in the comment area. Thanks and encourage each other.


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:JS—arrayNext article:JS—array