Funktionsbindung ist wahrscheinlich das, worauf Sie am wenigsten achten, wenn Sie mit der Verwendung von JavaScript beginnen. Wenn Sie jedoch feststellen, dass Sie eine Lösung benötigen, um diesen Kontext in einer anderen Funktion beizubehalten, benötigen Sie in Wirklichkeit Function.prototype. bind(), aber Sie bemerken dies möglicherweise immer noch nicht.
Wenn Sie zum ersten Mal auf dieses Problem stoßen, könnten Sie versucht sein, es auf eine Variable zu setzen, damit Sie nach einer Änderung des Kontexts weiterhin darauf verweisen können. Viele Leute entscheiden sich dafür, self, _this oder context als Variablennamen zu verwenden (manche verwenden auch diesen). Diese Methoden sind alle nützlich und natürlich ist daran nichts auszusetzen. Aber es gibt einen besseren, spezialisierteren Weg.
Was ist das eigentliche Problem, das wir lösen müssen?
Im folgenden Beispielcode können wir den Kontext legitim in einer Variablen zwischenspeichern:
var myObj = { specialFunction: function () { }, anotherSpecialFunction: function () { }, getAsyncData: function (cb) { cb(); }, render: function () { var that = this; this.getAsyncData(function () { that.specialFunction(); that.anotherSpecialFunction(); }); } }; myObj.render();
Wenn wir einfach this.specialFunction() verwenden, um die Methode aufzurufen, erhalten wir die folgende Fehlermeldung:
Ungefangener TypeError: Objekt [object global] hat keine Methode „specialFunction“
Für die Ausführung der Callback-Funktion müssen wir einen Verweis auf den myObj-Objektkontext behalten. Durch den Aufruf von that.specialFunction() können wir den Bereichskontext beibehalten und unsere Funktion korrekt ausführen. Es gibt jedoch einen einfacheren und saubereren Weg mit Function.prototype.bind():
render: function () { this.getAsyncData(function () { this.specialFunction(); this.anotherSpecialFunction(); }.bind(this)); }
Was haben wir gerade gemacht?
.bind() erstellt eine Funktion. Wenn diese Funktion aufgerufen wird, wird ihr Schlüsselwort this auf den übergebenen Wert gesetzt (bezieht sich hier auf den Parameter, der beim Aufruf von bind() übergeben wird). Daher übergeben wir den gewünschten Kontext, this (eigentlich myObj), an die Funktion .bind(). Wenn dann die Rückruffunktion ausgeführt wird, zeigt diese auf das myObj-Objekt.
Wenn Sie wissen möchten, wie Function.prototype.bind() intern aussieht und wie es funktioniert, finden Sie hier ein sehr einfaches Beispiel:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope); }; }
Es gibt auch einen ganz einfachen Anwendungsfall:
var foo = { x: 3 } var bar = function(){ console.log(this.x); } bar(); // undefined var boundFunc = bar.bind(foo); boundFunc(); // 3
Wir erstellen eine neue Funktion und wenn sie ausgeführt wird, wird diese auf foo gesetzt – nicht auf den globalen Gültigkeitsbereich wie beim Aufruf von bar().
Browser-Unterstützung
Unterstützung der Browserversion
Chrome 7
Firefox (Gecko) 4.0 (2)
Internet Explorer 9
Oper 11,60
Safari 5.1.4
Wie Sie sehen, wird Function.prototype.bind in IE8 und niedriger leider nicht unterstützt. Wenn Sie also keinen Fallback haben, kann es zu Problemen kommen.
Glücklicherweise bietet das Mozilla Developer Network (eine großartige Ressourcenbibliothek) eine solide Alternative für Browser, die die .bind()-Methode nicht selbst implementieren:
if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; }
Anwendbare Modi
Beim Erlernen technischer Punkte habe ich festgestellt, dass es nicht nur nützlich ist, das Konzept gründlich zu studieren und zu verstehen, sondern auch zu sehen, ob es in der vorliegenden Arbeit irgendeine Anwendung oder etwas Ähnliches gibt. Ich hoffe, dass einige der folgenden Beispiele auf Ihren Code zutreffen oder Probleme lösen, mit denen Sie konfrontiert sind.
CLICK HANDLERS (Klick-Handler-Funktion)
Eine Verwendung besteht darin, ein Klickereignis aufzuzeichnen (oder eine Aktion nach einem Klick auszuführen), was möglicherweise erfordert, dass wir einige Informationen in einem Objekt speichern, wie zum Beispiel:
var logger = { x: 0, updateCount: function(){ this.x++; console.log(this.x); } }
Wir können die Klickverarbeitungsfunktion wie folgt angeben und dann die updateCount()-Methode im Logger-Objekt aufrufen.
document.querySelector('button').addEventListener('click', function(){ logger.updateCount(); });
Aber wir müssen eine zusätzliche anonyme Funktion erstellen, um sicherzustellen, dass das Schlüsselwort this in der Funktion updateCount() den richtigen Wert hat.
Wir können den folgenden saubereren Weg verwenden:
document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));
Wir verwenden geschickt die praktische Funktion .bind(), um eine neue Funktion zu erstellen und ihren Gültigkeitsbereich an das Logger-Objekt zu binden.
SETTIMEOUT
Wenn Sie eine Template-Engine (z. B. Handlers) oder insbesondere einige der MV*-Frameworks (aus meiner Erfahrung kann ich nur über Backbone.js sprechen) verwendet haben, kennen Sie möglicherweise die folgende Diskussion über den Zugriff auf das neue DOM unmittelbar nach dem Rendern Vorlage Bei der Verwendung von Knoten sind Probleme aufgetreten.
Angenommen, wir möchten ein jQuery-Plugin instanziieren:
var myView = { template: '/* 一个包含 <select /> 的模板字符串*/', $el: $('#content'), afterRender: function () { this.$el.find('select').myPlugin(); }, render: function () { this.$el.html(this.template()); this.afterRender(); } } myView.render();
Vielleicht stellen Sie fest, dass es funktioniert – aber nicht jedes Mal, weil es Probleme gibt. Es ist eine Frage des Wettbewerbs: Wer zuerst dort ankommt, gewinnt. Manchmal steht das Rendern an erster Stelle und manchmal steht die Instanziierung des Plugins an erster Stelle. [Anmerkung des Übersetzers: Wenn der Rendervorgang noch nicht abgeschlossen ist (der DOM-Knoten wurde nicht zum DOM-Baum hinzugefügt), kann find('select') den entsprechenden Knoten nicht finden, um eine Instanziierung durchzuführen. 】
Nun wissen vielleicht nicht viele Leute, dass wir einen kleinen Hack basierend auf setTimeout() verwenden können, um das Problem zu lösen.
Lassen Sie uns unseren Code leicht umschreiben, um unser jQuery-Plug-in sicher zu instanziieren, nachdem der DOM-Knoten geladen wurde:
afterRender: function () { this.$el.find('select').myPlugin(); }, render: function () { this.$el.html(this.template()); setTimeout(this.afterRender, 0); }
然而,我们获得的是 函数 .afterRender() 不能找到 的错误信息。
我们接下来要做的,就是将.bind()使用到我们的代码中:
// afterRender: function () { this.$el.find('select').myPlugin(); }, render: function () { this.$el.html(this.template()); setTimeout(this.afterRender.bind(this), 0); } //
以上所述就是本文的全部内容了,希望大家能够喜欢。