Home >Web Front-end >JS Tutorial >Continue to learn javascript closure_javascript skills

Continue to learn javascript closure_javascript skills

WBOY
WBOYOriginal
2016-05-16 15:27:55984browse

1. What is closure?

The official explanation is: a closure is an expression (usually a function) that has many variables and an environment bound to these variables, so these variables are also part of the expression.
I believe that few people can directly understand this sentence because his description is too academic. In fact, this sentence in layman's terms means: all functions in JavaScript are closures. But generally speaking, the closure generated by nested functions is more powerful, and it is what we call "closure" most of the time. Look at the following code:

function a() { 
 var i = 0; 
 function b() { alert(++i); } 
 return b;
}
var c = a();
c();

This code has two characteristics:

1. Function b is nested inside function a;

2. Function a returns function b.

The reference relationship is as shown in the figure:

In this way, after executing var c=a(), variable c actually points to function b. After executing c() again, a window will pop up to display the value of i (the first time is 1). This code actually creates a closure. Why? Because variable c outside function a refers to function b within function a, that is:

 When the internal function b of function a is referenced by a variable outside function a, a closure is created.

Let’s be more thorough. The so-called "closure" is to define another function in the constructor body as the method function of the target object, and the method function of this object in turn refers to the temporary variable in the outer function body. This allows the temporary variable values ​​used by the original constructor body to be indirectly maintained as long as the target object can always maintain its methods during its lifetime. Although the initial constructor call has ended and the name of the temporary variable has disappeared, the value of the variable can always be referenced within the method of the target object, and the value can only be accessed through this method. Even if the same constructor is called again, only new objects and methods will be generated, and the new temporary variables only correspond to new values, which are independent from the last call.

2. What is the function of closure?

In short, the function of the closure is that after a is executed and returned, the closure prevents Javascript's garbage collection mechanism GC from reclaiming the resources occupied by a, because the execution of a's internal function b needs to depend on a. variables in . This is a very straightforward description of the role of closures. It is not professional or rigorous, but the general meaning is this. Understanding closures requires a step-by-step process.

In the above example, due to the existence of closure, i in a will always exist after function a returns. In this way, every time c() is executed, i will be the value of i that is alerted after adding 1.

Then let’s imagine another situation. If a returns something other than function b, the situation is completely different. Because after a is executed, b is not returned to the outside world of a, but is only referenced by a. At this time, a will only be referenced by b. Therefore, functions a and b refer to each other but are not disturbed by the outside world (referenced by the outside world). , functions a and b will be recycled by GC. (The garbage collection mechanism of Javascript will be introduced in detail later)

3. The microscopic world within the closure

If we want to have a deeper understanding of closures and the relationship between function a and nested function b, we need to introduce several other concepts: function execution context (execution context), active object (call object), scope (scope) ), scope chain. Take the process of function a from definition to execution as an example to illustrate these concepts.

  • When defining function a, the js interpreter will set the scope chain (scope chain) of function a to the "environment" where a is when defining a. If a is a global function, then there is only the window object in the scope chain.
  • When executing function a, a will enter the corresponding execution context.
  • In the process of creating the execution environment, a scope attribute will first be added to a, that is, the scope of a , and the value of is the scope chain in step 1. That is, the scope chain of a.scope=a.
  • Then the execution environment will create a call object. The active object is also an object that has properties, but it does not have a prototype and cannot be accessed directly through JavaScript code. After creating the active object, add the active object to the top of a's scope chain. At this time, a's scope chain contains two objects: a's active object and the window object.
  • The next step is to add an arguments attribute on the active object, which holds the parameters passed when calling function a.
  • Finally, add all formal parameters of function a and references to internal function b to the active object of a. In this step, the definition of function b is completed, so just like step 3, the scope chain of function b is set to the environment where b is defined, that is, the scope of a.

At this point, the steps from definition to execution of the entire function a are completed. At this time, a returns the reference of function b to c, and the scope chain of function b contains a reference to the active object of function a, which means that b can access all variables and functions defined in a. Function b is referenced by c, and function b depends on function a, so function a will not be recycled by GC after returning.

When function b is executed, it will be the same as the above steps. Therefore, the scope chain of b during execution contains 3 objects: b’s active object, a’s active object and window object, as shown in the following figure:

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

  • 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  • 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  • 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) { 
 var g = function () { return x; }
 return g;
}
var h = f(1);
alert(h()); 

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。
假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。
如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

四、闭包的应用场景
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
私有属性和方法在Constructor外是无法被访问的

function Constructor(...) { 
 var that = this; 
 var membername = value; 
 function membername(...) {...}
}

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

五、Javascript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

六、结语

理解了aScript的闭包的解释和运行机制才能写出更为安全和优雅的代码,希望对大家的学习有所帮助。

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