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)();
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
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
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
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; }
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.
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>
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; }
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; }; }
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"
没有人愿意不经同意就被别人改名字,为了阻止这种情况的发生,通过闭包让_name字段变成私有。看如下代码,注意这里的_name是Person构造器的本地变量,而不是对象的属性,闭包形成了,因为外层方法Person对外暴露了一个内部方法getName。
function Person(name) { var _name = name;// 注:区别在这里 this.getName = function() { return _name; }; }
现在,当getName被调用,能够保证返回的是最初传入类构造器的值。我们依然可以为对象添加新的_name属性,但这并不影响闭包getName最初绑定的值,下面的代码证明,_name字段,事实私有。
var person = new Person("Colin"); person._name = "Tom"; // person._name is "Tom" but person.getName() returns "Colin"
什么时候不要用闭包?
正确理解闭包如何工作何时使用非常重要,而理解什么时候不应该用它也同样重要。过度使用闭包会导致脚本执行变慢并消耗额外内存。由于闭包太容易创建了,所以很容易发生你都不知道怎么回事,就已经创建了闭包的情况。本节我们说几种场景要注意避免闭包的产生。
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>
去解决这个问题,必须在循环中去掉闭包(译者:这里的闭包指的是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)); } });
2.构造函数里的非必要使用
类的构造函数里,也是经常会产生闭包的错误使用。我们已经知道如何通过闭包设置类的私有属性,而如果当一个方法不需要调用私有属性,则造成的闭包是浪费的。下面的例子中,Person类增加了sayHello方法,但是它没有使用私有属性。
function Person(name) { var _name = name; this.getName = function() { return _name; }; this.sayHello = function() { alert("Hello!"); }; }
每当Person被实例化,创建sayHello都要消耗时间,想象一下有大量的Person被实例化。更好的实践是将sayHello放入Person的原型链里(prototype),原型链里的方法,会被所有的实例化对象共享,因此节省了为每个实例化对象去创建一个闭包(译者:指sayHello),所以我们有必要做如下修改:
function Person(name) { var _name = name; this.getName = function() { return _name; }; } Person.prototype.sayHello = function() { alert("Hello!"); };
需要记得一些事情
闭包包含了一个方法,以及创建它的代码环境引用
闭包会在外部函数包含内部函数的情况下形成
闭包可以轻松的帮助回调函数传入参数
类的私有属性可以通过闭包模拟
类的构造器中使用闭包不是一个好主意,将它们放到原型链中
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!