Heim > Web-Frontend > js-Tutorial > Hauptteil

Bringt Sie zum besseren Verständnis von js-Verschlüssen (Details)

不言
Freigeben: 2018-10-18 13:39:18
Original
2413 Leute haben es durchsucht

Der Inhalt dieses Artikels soll Ihnen helfen, js-Abschlüsse besser zu verstehen (im Detail). Ich hoffe, dass er für Freunde in Not hilfreich ist.

Übersetzer: Abschlüsse wurden so oft diskutiert, dass es mir peinlich ist zu sagen, dass ich JS kenne, auch wenn ich Abschlüsse nicht verstehe. Aber als ich diesen Artikel las, leuchteten meine Augen auf und Es hat mich auch dazu gebracht, über Abschlüsse nachzudenken, und es beinhaltet einige Kenntnisse über Klassen und Prototypketten. Es ist ein wenig früh und der Inhalt ist etwas einfach, aber es ist sehr Ich hoffe, dass es den Lesern neues Verständnis bringen kann.

Das Schließen ist eine etwas komplexe und missverstandene Funktion der JavaScript-Sprache. Einfach ausgedrückt ist ein Abschluss ein Objekt, das eine Methode (Funktion) und einen Verweis auf die Umgebung zum Zeitpunkt der Erstellung der Methode enthält. Um den Verschluss vollständig zu verstehen, müssen wir auch zwei Funktionen in js verstehen, eines ist die erstklassige Funktion und das andere ist die innere Funktion.

Erstklassige Funktionen

In js ist eine Methode ein erstklassiger Bürger, da sie leicht in andere Datentypen konvertiert werden kann. Beispielsweise kann eine Methode der ersten Ebene spontan erstellt und einer Variablen zugewiesen werden. Es kann auch an andere Methoden übergeben oder über andere Methoden zurückgegeben werden. Methoden erfüllen nicht nur diese Kriterien, sondern verfügen auch über eigene Eigenschaften und Methoden.
Lassen Sie uns anhand des folgenden Beispiels einen Blick auf die Funktionen der Methode der ersten Ebene werfen.

var foo = function() {
  alert("Hello World!");
};

var bar = function(arg) {
  return arg;
};

bar(foo)();
Nach dem Login kopieren
Anmerkung des Übersetzers: Wenn man die Texterklärung des Codes im Originaltext weglässt, spiegelt sich hier wider, dass die Methode der ersten Ebene Parameter zurückgeben kann, die Parameter eine andere Funktion der ersten Ebene sein können und das zurückgegebene Ergebnis auch aufgerufen werden.

Innere Methoden/Innere Funktionen

Interne Methoden oder verschachtelte Methoden beziehen sich auf Methoden, die in anderen Methoden definiert sind. Immer wenn die externe Methode aufgerufen wird, wird die Instanz der internen Methode erstellt. Das folgende Beispiel spiegelt die Verwendung interner Methoden wider. Die add-Methode ist eine externe Methode und doAdd ist eine interne Methode.

function add(value1, value2) {
  function doAdd(operand1, operand2) {
    return operand1 + operand2;
  }

  return doAdd(value1, value2);
}

var foo = add(1, 2);
// foo equals 3
Nach dem Login kopieren

In diesem Beispiel besteht ein wichtiges Merkmal darin, dass die interne Methode den Umfang der externen Methode erhält, was bedeutet, dass die interne Methode die Variablen, Parameter usw. der externen Methode verwenden kann. Im Beispiel werden die Parameter value1 und value2 von add() an die Parameter operand1 und operand2 von doAdd() übergeben. Dies ist jedoch nicht erforderlich, da doAdd Wert1 und Wert2 direkt abrufen kann. Wir können das obige Beispiel also auch so schreiben:

function add(value1, value2) {
  function doAdd() {
    return value1 + value2;
  }

  return doAdd();
}

var foo = add(1, 2);
// foo equals 3
Nach dem Login kopieren

Abschlüsse erstellen/Abschlüsse erstellen

Die interne Methode erhält den Umfang der externen Methode und bildet einen Abschluss. Ein typisches Szenario besteht darin, dass die externe Funktion ihre interne Methode zurückgibt, die einen Verweis auf die externe Umgebung beibehält und alle Variablen im Gültigkeitsbereich speichert.
Das folgende Beispiel zeigt, wie Verschlüsse erstellt und verwendet werden.

function add(value1) {
  return function doAdd(value2) {
    return value1 + value2;
  };
}

var increment = add(1);
var foo = increment(2);
// foo equals 3
Nach dem Login kopieren

Erklärung:

  • add gibt die interne Methode doAdd zurück, doAdd ruft die Parameter von add auf und der Abschluss wird erstellt.

  • value1 ist eine lokale Variable der Add-Methode, die eine nicht lokale Variable für doAdd ist (eine nicht lokale Variable bedeutet, dass sich die Variable weder im Funktionskörper selbst noch in befindet die globale Welt) und value2 ist eine lokale Variable von doAdd .

  • Wenn add(1) aufgerufen wird, wird ein Abschluss erstellt und inkrementell gespeichert. In der Referenzumgebung des Abschlusses ist Wert1 an 1 gebunden, und die Grenze 1 entspricht „Blockieren“ in dieser Funktion, woher auch der Name „Abschluss“ stammt.

  • Wenn Inkrement (2) aufgerufen wird, wird die Abschlussfunktion eingegeben. Dies bedeutet, dass doAdd mit dem Wert 1 aufgerufen wird, sodass der Abschluss im Wesentlichen als die folgende Funktion betrachtet werden kann:

function increment(value2) {
  return 1 + value2;
}
Nach dem Login kopieren

Wann sollte man Verschlüsse verwenden?

Verschlüsse können viele Funktionen erfüllen. Binden Sie beispielsweise die Callback-Funktion an bestimmte Parameter. Lassen Sie uns über zwei Szenarien sprechen, die Ihr Leben und Ihre Entwicklung einfacher machen.

  1. Arbeiten mit Timern

Abschlüsse sind in Kombination mit setTimeout und setInterval sehr nützlich. Mit Abschlüssen können Sie bestimmte Parameter an die Callback-Funktion übergeben. Beispiel: Fügen Sie jede Sekunde eine Zeichenfolge in den angegebenen Dom ein.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      window.setInterval(showMessage, 1000, "some message<br />");
    });

    function showMessage(message) {
      document.getElementById("message").innerHTML += message;
    }
  </script>
</head>
<body>
  <span id="message"></span>
</body>
</html>
Nach dem Login kopieren

Leider unterstützt der IE die Übergabe von Parametern an den setInterval-Callback nicht. Die Seite im IE zeigt nicht „irgendeine Nachricht“ an, sondern „undefiniert“ (es wird kein Wert an showMessage() übergeben). , Der erwartete Wert kann durch einen Abschluss an die Rückruffunktion gebunden werden. Wir können den obigen Code umschreiben:

window.addEventListener("load", function() {
  var showMessage = getClosure("some message<br />");

  window.setInterval(showMessage, 1000);
});

function getClosure(message) {
  function showMessage() {
    document.getElementById("message").innerHTML += message;
  }

  return showMessage;
}
Nach dem Login kopieren

2. Simulieren Sie private Eigenschaften
Die meisten objektorientierten Programmiersprachen unterstützen private Eigenschaften von Objekten , aber js Es ist keine reine objektorientierte Sprache, daher gibt es kein Konzept für private Eigenschaften. Allerdings können wir durch Schließungen Privatgrundstücke simulieren. Denken Sie daran, dass ein Abschluss einen Verweis auf die Umgebung enthält, in der er erstellt wurde. Dieser Verweis befindet sich nicht mehr im aktuellen Bereich, sodass auf diesen Verweis nur innerhalb des Abschlusses zugegriffen werden kann. Dies ist im Wesentlichen eine private Eigenschaft.
Sehen Sie sich das folgende Beispiel an (Übersetzer: Textbeschreibung des Codes weglassen):

function Person(name) {
  this._name = name;

  this.getName = function() {
    return this._name;
  };
}
Nach dem Login kopieren

Hier liegt ein ernstes Problem vor, da js keine privaten Attribute unterstützt, sodass wir andere nicht daran hindern können, den Namen zu ändern Im Feld der Instanz erstellen wir beispielsweise eine Personeninstanz namens Colin und ändern dann seinen Namen in Tom.

var person = new Person("Colin");

person._name = "Tom";
// person.getName() now returns "Tom"
Nach dem Login kopieren

没有人愿意不经同意就被别人改名字,为了阻止这种情况的发生,通过闭包让_name字段变成私有。看如下代码,注意这里的_name是Person构造器的本地变量,而不是对象的属性,闭包形成了,因为外层方法Person对外暴露了一个内部方法getName。

function Person(name) {
  var _name = name;// 注:区别在这里

  this.getName = function() {
    return _name;
  };
}
Nach dem Login kopieren

现在,当getName被调用,能够保证返回的是最初传入类构造器的值。我们依然可以为对象添加新的_name属性,但这并不影响闭包getName最初绑定的值,下面的代码证明,_name字段,事实私有。

var person = new Person("Colin");

person._name = "Tom";
// person._name is "Tom" but person.getName() returns "Colin"
Nach dem Login kopieren

什么时候不要用闭包?

正确理解闭包如何工作何时使用非常重要,而理解什么时候不应该用它也同样重要。过度使用闭包会导致脚本执行变慢并消耗额外内存。由于闭包太容易创建了,所以很容易发生你都不知道怎么回事,就已经创建了闭包的情况。本节我们说几种场景要注意避免闭包的产生。
1.循环中
循环中创建出闭包会导致结果异常。下例中,页面上有三个按钮,分别点击弹出不同的话术。然而实际运行,所有的按钮都弹出button4的话术,这是因为,当按钮被点击时,循环已经执行完毕,而循环中的变量i也已经变成了最终值4.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      for (var i = 1; i < 4; i++) {
        var button = document.getElementById("button" + i);

        button.addEventListener("click", function() {
          alert("Clicked button " + i);
        });
      }
    });
  </script>
</head>
<body>
  <input type="button" id="button1" value="One" />
  <input type="button" id="button2" value="Two" />
  <input type="button" id="button3" value="Three" />
</body>
</html>
Nach dem Login kopieren

去解决这个问题,必须在循环中去掉闭包(译者:这里的闭包指的是click事件回调函数绑定了外层引用i),我们可以通过调用一个引用新环境的函数来解决。下面的代码中,循环中的变量传递给getHandler函数,getHandler返回一个闭包(译者:这个闭包指的是getHandler返回的内部方法绑定传入的i参数),独立于原来的for循环。

function getHandler(i) {
  return function handler() {
    alert("Clicked button " + i);
  };
}

window.addEventListener("load", function() {
  for (var i = 1; i < 4; i++) {
    var button = document.getElementById("button" + i);

    button.addEventListener("click", getHandler(i));
  }
});
Nach dem Login kopieren

2.构造函数里的非必要使用
类的构造函数里,也是经常会产生闭包的错误使用。我们已经知道如何通过闭包设置类的私有属性,而如果当一个方法不需要调用私有属性,则造成的闭包是浪费的。下面的例子中,Person类增加了sayHello方法,但是它没有使用私有属性。

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };

  this.sayHello = function() {
    alert("Hello!");
  };
}
Nach dem Login kopieren

每当Person被实例化,创建sayHello都要消耗时间,想象一下有大量的Person被实例化。更好的实践是将sayHello放入Person的原型链里(prototype),原型链里的方法,会被所有的实例化对象共享,因此节省了为每个实例化对象去创建一个闭包(译者:指sayHello),所以我们有必要做如下修改:

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Person.prototype.sayHello = function() {
  alert("Hello!");
};
Nach dem Login kopieren

需要记得一些事情

闭包包含了一个方法,以及创建它的代码环境引用

闭包会在外部函数包含内部函数的情况下形成

闭包可以轻松的帮助回调函数传入参数

类的私有属性可以通过闭包模拟

类的构造器中使用闭包不是一个好主意,将它们放到原型链中

Das obige ist der detaillierte Inhalt vonBringt Sie zum besseren Verständnis von js-Verschlüssen (Details). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:segmentfault.com
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage