Home > Web Front-end > JS Tutorial > JavaScript Closure Part 2: Implementation of Closure

JavaScript Closure Part 2: Implementation of Closure

黄舟
Release: 2016-12-20 16:07:47
Original
810 people have browsed it

After discussing the theoretical part, let us introduce how closures are implemented in ECMAScript. It’s worth emphasizing again here: ECMAScript only uses static (lexical) scope (while languages ​​such as Perl can use both static and dynamic scopes for variable declarations).

var x = 10;
 
function foo() {
  alert(x);
}
 
(function (funArg) {
 
  var x = 20;
 
  // 变量"x"在(lexical)上下文中静态保存的,在该函数创建的时候就保存了
  funArg(); // 10, 而不是20
 
})(foo);
Copy after login

Technically speaking, the data of the parent context that created the function is stored in the function's internal property [[Scope]]. If you don't know what [[Scope]] is, it is recommended that you read the previous chapter first, which gives a very detailed introduction to [[Scope]]. If you fully understand [[Scope]] and scope chain knowledge, then you will also fully understand closures.

According to the function creation algorithm, we see that in ECMAScript, all functions are closures, because they save the scope chain of the upper context when they are created (except for exceptions) (regardless of this function Whether it will be activated later - [[Scope]] is available when the function is created):

var x = 10;
 
function foo() {
  alert(x);
}
 
// foo是闭包
foo: <FunctionObject> = {
  [[Call]]: <code block of foo>,
  [[Scope]]: [
    global: {
      x: 10
    }
  ],
  ... // 其它属性
};
Copy after login

As we said, for optimization purposes, when a function does not use free variables, the implementation may not be saved in the side effect domain chain inside. However, nothing is said in the ECMA-262-3 specification. Therefore, normally, all parameters are saved in the [[Scope]] attribute during the creation phase.

Some implementations allow direct access to the closure scope. For example, Rhino, for the [[Scope]] attribute of the function, there is a non-standard __parent__ attribute:

var global = this;
var x = 10;
 
var foo = (function () {
 
  var y = 20;
 
  return function () {
    alert(y);
  };
 
})();
 
foo(); // 20
alert(foo.__parent__.y); // 20
 
foo.__parent__.y = 30;
foo(); // 30
 
// 可以通过作用域链移动到顶部
alert(foo.__parent__.__parent__ === global); // true
alert(foo.__parent__.__parent__.x); // 10
Copy after login

All objects refer to a [[Scope]]

Also note here: in ECMAScript, Closures created in the same parent context share a [[Scope]] attribute. In other words, a closure's modification of the variables in [[Scope]] will affect other closures' reading of its variables:

var firstClosure;
var secondClosure;
 
function foo() {
 
  var x = 1;
 
  firstClosure = function () { return ++x; };
  secondClosure = function () { return --x; };
 
  x = 2; // 影响 AO["x"], 在2个闭包公有的[[Scope]]中
 
  alert(firstClosure()); // 3, 通过第一个闭包的[[Scope]]
}
 
foo();
 
alert(firstClosure()); // 4
alert(secondClosure()); // 3
Copy after login

This means: all internal functions share the same parent scope

There is a very common misunderstanding about this function. Developers often do not get the expected results when creating functions in loop statements (counting internally), and the expectation is that each function has its own value.

var data = [];
 
for (var k = 0; k < 3; k++) {
  data[k] = function () {
    alert(k);
  };
}
 
data[0](); // 3, 而不是0
data[1](); // 3, 而不是1
data[2](); // 3, 而不是2
Copy after login

The above example proves that - closures created in the same context share a [[Scope]] attribute. Therefore the variable "k" in the upper context can be easily changed.

activeContext.Scope = [
  ... // 其它变量对象
  {data: [...], k: 3} // 活动对象
];
 
data[0].[[Scope]] === Scope;
data[1].[[Scope]] === Scope;
data[2].[[Scope]] === Scope;
Copy after login

In this way, when the function is activated, the final k used has become 3. As shown below, creating a closure can solve this problem:

var data = [];
 
for (var k = 0; k < 3; k++) {
  data[k] = (function _helper(x) {
    return function () {
      alert(x);
    };
  })(k); // 传入"k"值
}
 
// 现在结果是正确的了
data[0](); // 0
data[1](); // 1
data[2](); // 2
Copy after login

Let’s see what happens in the above code? After the function "_helper" is created, it is activated by passing in the parameter "k". Its return value is also a function, which is stored in the corresponding array element. This technique produces the following effects: When the function is activated, each time "_helper" creates a new variable object, which contains the parameter "x", and the value of "x" is the value of "k" passed in. In this way, the [[Scope]] of the returned function becomes as follows:

data[0].[[Scope]] === [
  ... // 其它变量对象
  父级上下文中的活动对象AO: {data: [...], k: 3},
  _helper上下文中的活动对象AO: {x: 0}
];
 
data[1].[[Scope]] === [
  ... // 其它变量对象
  父级上下文中的活动对象AO: {data: [...], k: 3},
  _helper上下文中的活动对象AO: {x: 1}
];
 
data[2].[[Scope]] === [
  ... // 其它变量对象
  父级上下文中的活动对象AO: {data: [...], k: 3},
  _helper上下文中的活动对象AO: {x: 2}
];
Copy after login

We see that at this time the [[Scope]] attribute of the function has the really desired value. In order to achieve this For this purpose, we have to create additional variable objects in [[Scope]]. It should be noted that in the returned function, if you want to get the value of "k", the value will still be 3.

By the way, a large number of articles introducing JavaScript believe that only additionally created functions are closures, which is wrong. In practice, this method is the most effective. However, from a theoretical point of view, all functions in ECMAScript are closures.

However, the methods mentioned above are not the only ones. The correct value of "k" can also be obtained in other ways, as follows:

var data = [];
 
for (var k = 0; k < 3; k++) {
  (data[k] = function () {
    alert(arguments.callee.x);
  }).x = k; // 将k作为函数的一个属性
}
 
// 结果也是对的
data[0](); // 0
data[1](); // 1
data[2](); // 2
Copy after login

Funarg and return

Another feature is returning from a closure. In ECMAScript, a return statement in a closure returns control flow to the calling context (the caller). In other languages, such as Ruby, there are many forms of closures, and the corresponding closure returns are also different. The following methods are possible: it may be returned directly to the caller, or in some cases --Exit directly from the context.

ECMAScript standard exit behavior is as follows:

function getElement() {
 
  [1, 2, 3].forEach(function (element) {
 
    if (element % 2 == 0) {
      // 返回给函数"forEach"函数
      // 而不是返回给getElement函数
      alert(&#39;found: &#39; + element); // found: 2
      return element;
    }
 
  });
 
  return null;
}
Copy after login

However, in ECMAScript, the following effect can be achieved through try catch:

var $break = {};
 
function getElement() {
 
  try {
 
    [1, 2, 3].forEach(function (element) {
 
      if (element % 2 == 0) {
        // // 从getElement中"返回"
        alert(&#39;found: &#39; + element); // found: 2
        $break.data = element;
        throw $break;
      }
 
    });
 
  } catch (e) {
    if (e == $break) {
      return $break.data;
    }
  }
 
  return null;
}
 
alert(getElement()); // 2
Copy after login

Theoretical version

To explain here, developers often mistakenly simplify closures and understand them from the parent context Returning internal functions even understands that only anonymous functions can be closures.

Again, because of the scope chain, all functions are closures (regardless of function type: anonymous functions, FE, NFE, and FD are all closures).

There is only one type of function except that, that is the function created through the Function constructor, because its [[Scope]] only contains global objects. In order to better clarify this issue, we give two correct version definitions of closures in ECMAScript:

In ECMAScript, closures refer to:

From a theoretical perspective: all functions. Because they all save the data of the upper context when they are created. This is true even for simple global variables, because accessing global variables in a function is equivalent to accessing free variables. At this time, the outermost scope is used.

From a practical perspective: The following functions are considered closures:

Even if the context in which it was created has been destroyed, it still exists (for example, the inner function returns from the parent function)

Free variables are referenced in the code

That’s it JavaScript Closure Part 2: The content of closure implementation. For more related content, please pay attention to the PHP Chinese website (www.php.cn)!


Related labels:
source:php.cn
Statement of this Website
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
Latest Issues
What are JavaScript hook functions?
From 1970-01-01 08:00:00
0
0
0
What is JavaScript garbage collection?
From 1970-01-01 08:00:00
0
0
0
c++ calls javascript
From 1970-01-01 08:00:00
0
0
0
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template