Heim >Web-Frontend >js-Tutorial >JavaScript-Verschlüsse Teil 1: Einführung in Verschlüsse

JavaScript-Verschlüsse Teil 1: Einführung in Verschlüsse

黄舟
黄舟Original
2016-12-20 16:03:21971Durchsuche

Ich habe Bereichsketten und variable Objekte bereits früher eingeführt, und jetzt ist es leicht zu verstehen, wenn wir über Abschlüsse sprechen. Eigentlich hat schon jeder von Schließungen gesprochen. Nichtsdestotrotz werden wir hier versuchen, Abschlüsse aus theoretischer Sicht zu diskutieren und zu sehen, wie Abschlüsse in ECMAScript tatsächlich intern funktionieren.

Bevor ECMAScript-Abschlüsse direkt besprochen werden, ist es noch notwendig, sich einige grundlegende Definitionen in der funktionalen Programmierung anzusehen.

Wie wir alle wissen, sind Funktionen in funktionalen Sprachen (ECMAScript unterstützt diesen Stil auch) Daten. Beispielsweise können Funktionen Variablen zugewiesen, als Parameter an andere Funktionen übergeben, von Funktionen zurückgegeben werden usw. Solche Funktionen haben spezielle Namen und Strukturen.

Definition

Ein funktionales Argument („Funarg“) – ist ein Argument, dessen Wert eine Funktion ist und die Parameter der Funktion sind.

Beispiel:

Der eigentliche Parameter von funarg im obigen Beispiel ist tatsächlich die anonyme Funktion, die an exampleFunc übergeben wird.
function exampleFunc(funArg) {
  funArg();
}
 
exampleFunc(function () {
  alert('funArg');
});

Umgekehrt wird eine Funktion, die Funktionsparameter akzeptiert, als Funktion hoher Ordnung (kurz HOF) bezeichnet. Es kann auch genannt werden: funktionale Funktion oder partielle mathematische Theorie oder Operator. Im obigen Beispiel ist exampleFunc eine solche Funktion.

Wie bereits erwähnt, können Funktionen nicht nur als Parameter, sondern auch als Rückgabewerte verwendet werden. Solche Funktionen, die eine Funktion zurückgeben, werden Funktionen mit Funktionswert oder Funktionswertfunktionen genannt.

Funktionen, die in Form normaler Daten existieren können (z. B. wenn Parameter übergeben werden, Funktionsparameter akzeptieren oder Funktionswerte zurückgeben), werden erstklassige Funktionen (im Allgemeinen erstklassige Objekte) genannt. In ECMAScript sind alle Funktionen erstklassige Objekte.
(function functionValued() {
  return function () {
    alert('returned function is called');
  };
})()();

Funktionen, die als normale Daten existieren können (z. B. wenn Parameter übergeben werden, Funktionsparameter akzeptieren oder Funktionswerte zurückgeben), werden als erstklassige Funktionen (im Allgemeinen erstklassige Objekte) bezeichnet.

In ECMAScript sind alle Funktionen erstklassige Objekte.

Eine Funktion, die sich selbst als Parameter akzeptiert, wird als autoapplikative Funktion (autoapplikative Funktion oder selbstapplikative Funktion) bezeichnet:

Eine Funktion, die sich selbst als Rückgabe akzeptiert Der Wert wird als Selbstreplikationsfunktion (autoreplizierende Funktion oder selbstreplizierende Funktion) bezeichnet. Normalerweise wird in der Literatur der Begriff „selbstreplizierend“ verwendet:
(function selfApplicative(funArg) {
 
  if (funArg && funArg === selfApplicative) {
    alert('self-applicative');
    return;
  }
 
  selfApplicative(selfApplicative);
 
})();

Eines der interessanteren Muster selbstreplizierender Funktionen besteht darin, nur ein Element einer Menge als Argument zu akzeptieren, anstatt das zu akzeptieren sich einstellen.
(function selfReplicative() {
  return selfReplicative;
})();

Aber die direkte Weitergabe der Sammlung ist relativ effektiv und intuitiv.
// 接受集合的函数
function registerModes(modes) {
  modes.forEach(registerMode, modes);
}
 
// 用法
registerModes(['roster', 'accounts', 'groups']);
 
// 自复制函数的声明
function modes(mode) {
  registerMode(mode); // 注册一个mode
  return modes; // 返回函数自身
}
 
// 用法,modes链式调用
modes('roster')('accounts')('groups')
 
//有点类似:jQueryObject.addClass("a").toggle().removClass("b")

Auf in Funktionsparametern definierte Variablen kann zugegriffen werden, wenn „funarg“ aktiviert ist (da das Variablenobjekt, das Kontextdaten speichert, jedes Mal erstellt wird, wenn der Kontext eingegeben wird):

Allerdings in Mit ECMAScript können Funktionen in übergeordnete Funktionen gekapselt werden und Variablen aus dem Kontext der übergeordneten Funktion verwenden. Diese Funktion kann zu Funarg-Problemen führen.
function testFn(funArg) {
  // funarg激活时, 局部变量localVar可以访问了
  funArg(10); // 20
  funArg(20); // 30
 
}
 
testFn(function (arg) {
  var localVar = 10;
  alert(arg + localVar);
});

Funarg-Problem

In einer stapelorientierten Programmiersprache werden die lokalen Variablen einer Funktion auf dem Stapel gespeichert, wenn die Funktion aktiviert wird der Stapel.

Wenn die Funktion zurückkehrt, werden diese Parameter vom Stapel entfernt. Dieses Modell schränkt die Verwendung von Funktionen als Funktionswerte (z. B. als Rückgabewerte von übergeordneten Funktionen) erheblich ein. Meistens treten Probleme auf, wenn Funktionen freie Variablen haben.

Eine freie Variable bezieht sich auf eine Variable, die in einer Funktion verwendet wird, aber weder ein Funktionsparameter noch eine lokale Variable der Funktion ist

Beispiel:

In Das obige Beispiel: Für die innerFn-Funktion ist localVar eine freie Variable.
function testFn() {
 
  var localVar = 10;
 
  function innerFn(innerParam) {
    alert(innerParam + localVar);
  }
 
  return innerFn;
}
 
var someFn = testFn();
someFn(20); // 30

Für Systeme, die ein stapelorientiertes Modell zum Speichern lokaler Variablen verwenden, bedeutet dies, dass bei Beendigung des testFn-Funktionsaufrufs seine lokalen Variablen vom Stapel entfernt werden. Auf diese Weise tritt ein Fehler auf, wenn der Funktionsaufruf von innerFn von außen erfolgt (da die Variable localVar nicht mehr vorhanden ist).

Darüber hinaus ist es im obigen Beispiel im stapelorientierten Implementierungsmodell einfach unmöglich, innerFn als Rückgabewert zurückzugeben. Da es sich auch um eine lokale Variable der testFn-Funktion handelt, wird sie auch entfernt, wenn testFn zurückkehrt.

Ein weiteres Problem besteht, wenn das System einen dynamischen Bereich verwendet und Funktionen als Funktionsparameter verwendet werden.

Sehen Sie sich das folgende Beispiel (Pseudocode) an:

Wir sehen, dass mithilfe des dynamischen Bereichs das System von Variablen (Identifikatoren) durch den dynamischen Stapel von Variablen verwaltet wird. Daher werden freie Variablen in der aktuell aktiven dynamischen Kette abgefragt und nicht in der statischen Bereichskette, die beim Erstellen der Funktion gespeichert wurde.
var z = 10;
 
function foo() {
  alert(z);
}
 
foo(); // 10 – 使用静态和动态作用域的时候
 
(function () {
 
  var z = 20;
  foo(); // 10 – 使用静态作用域, 20 – 使用动态作用域
 
})();
 
// 将foo作为参数的时候是一样的
(function (funArg) {
 
  var z = 30;
  funArg(); // 10 – 静态作用域, 30 – 动态作用域
 
})(foo);

这样就会产生冲突。比方说,即使Z仍然存在(与之前从栈中移除变量的例子相反),还是会有这样一个问题: 在不同的函数调用中,Z的值到底取哪个呢(从哪个上下文,哪个作用域中查询)?

上述描述的就是两类funarg问题 —— 取决于是否将函数以返回值返回(第一类问题)以及是否将函数当函数参数使用(第二类问题)。

为了解决上述问题,就引入了 闭包的概念。

闭包

闭包是代码块和创建该代码块的上下文中数据的结合。

让我们来看下面这个例子(伪代码):

var x = 20;
 
function foo() {
  alert(x); // 自由变量"x" == 20
}
 
// 为foo闭包
fooClosure = {
  call: foo // 引用到function
  lexicalEnvironment: {x: 20} // 搜索上下文的上下文
};

上述例子中,“fooClosure”部分是伪代码。对应的,在ECMAScript中,“foo”函数已经有了一个内部属性——创建该函数上下文的作用域链。

“lexical”通常是省略的。上述例子中是为了强调在闭包创建的同时,上下文的数据就会保存起来。当下次调用该函数的时候,自由变量就可以在保存的(闭包)上下文中找到了,正如上述代码所示,变量“z”的值总是10。

定义中我们使用的比较广义的词 —— “代码块”,然而,通常(在ECMAScript中)会使用我们经常用到的函数。当然了,并不是所有对闭包的实现都会将闭包和函数绑在一起,比方说,在Ruby语言中,闭包就有可能是: 一个过程对象(procedure object), 一个lambda表达式或者是代码块。

对于要实现将局部变量在上下文销毁后仍然保存下来,基于栈的实现显然是不适用的(因为与基于栈的结构相矛盾)。因此在这种情况下,上层作用域的闭包数据是通过 动态分配内存的方式来实现的(基于“堆”的实现),配合使用垃圾回收器(garbage collector简称GC)和 引用计数(reference counting)。这种实现方式比基于栈的实现性能要低,然而,任何一种实现总是可以优化的: 可以分析函数是否使用了自由变量,函数式参数或者函数式值,然后根据情况来决定 —— 是将数据存放在堆栈中还是堆中。

 以上就是JavaScript闭包其一:闭包概论的内容,更多相关内容请关注PHP中文网(www.php.cn)! 


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn