5 Stimmen

Bindung von "this" bei der Übergabe der Mitgliedsfunktion eines Objekts

Ich hatte eine "Klasse" definiert und nur eine Instanz davon erstellt. Die Instanz besaß eine Mitgliedsfunktion, die am Ende herumgereicht werden würde (es ist ein Maus-Handler, aber das ist nicht wichtig). Da ich immer nur eine Instanz meiner "Klasse" erstellen würde, beschloss ich, sie als Singleton umzuschreiben, indem ich ein Objektliteral verwendete.

Ich habe also

var mySingleton = {
    theObjects : [];
}

mySingleton.mouseHandler = (function() {
    var that = this;
    return function (e) {
        for (var indx = 0; indx < that.theObjects.length; indx++) {
            // do something to that.theObjects[indx];
        }
    }
}());

mySingleton.addObject = function(newObj) {
    this.theObjects.push(newObj);
}

Wenn ich jedoch versuche, diesen Code zu verwenden (nachdem ich ein paar Objekte hinzugefügt habe), erhalte ich ständig die Fehlermeldung that.theObjects is undefined. Er bezieht sich auf die Zeile in der for-Schleife.

9voto

gilly3 Punkte 83450

Update für 2015 - Verwendung Function.bind() um den Wert von this innerhalb der Funktion. Dann wird statt der Verwendung von that können Sie verwenden this .

mySingleton.mouseHandler = function (e) {
    for (var indx = 0; indx < this.theObjects.length; indx++) {
        // do something to this.theObjects[indx];
    }
}.bind(mySingleton);

Dies funktioniert nicht, wenn Sie mouseHandler um den Kontext des Elements "moused" zu haben. Verwenden Sie dazu meine ursprüngliche Antwort unten.

Wenn Sie IE8 oder (Gott bewahre) frühere Versionen unterstützen müssen, müssen Sie eine Polyfill .


Da Sie die Funktion aufrufen, die die mouseHandler wird es sofort im Kontext von window pas mySingleton . Also that bezieht sich auf window . Anstatt sie sofort aufzurufen, ändern Sie sie einfach in eine Methode um, so dass sie im Kontext von mySingleton :

mySingleton.getMouseHandler = function() {
    var that = this;
    return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();

Da Sie bereits ein Singleton verwenden, können Sie es natürlich auch direkt referenzieren. In Ihrem Click-Handler, statt der Überprüfung that.theObjects , prüfen mySingleton.theObjects . Oder, in mouseHandler ändern var that = this a var that = mySingleton .

Editar: Oder Sie übergeben den Kontext an Ihre anonyme Funktion, wenn Sie sie aufrufen:

mySingleton.mouseHandler = (function() {
    var that = this;
    return function (e) {
        for (var indx = 0; indx < that.theObjects.length; indx++) {
            // do something to that.theObjects[indx];
        }
    }
}).call(mySingleton);

1voto

Peter Lyons Punkte 137811

Dafür gibt es einige beliebte Methoden. Die erste, supereinfache Lösung ist, mySingleton direkt zu referenzieren und die Verwirrung zu umgehen, die mit this . Anstelle von that.theObjects einfach tun mySingleton.theObjects und machen Sie weiter mit Ihrem Leben und alles wird gut.

Es gibt jedoch ein gemeinsames Muster für diese Bindung. Hier ist wie underscore.js das macht

Sehen Sie sich die kommentierte Quelle zu Unterstrich , wo Sie Folgendes finden

 _.bind = function(func, obj) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(obj, args.concat(slice.call(arguments)));
    };
  };

0voto

jimbo Punkte 10756

Auch die anderen Antworten, die bisher gegeben wurden, sind richtig. Ich stelle hier meine Sichtweise dar, falls es hilft.

Um zu verstehen, warum sich der Code nicht so verhält, wie Sie es erwarten, müssen Sie verstehen, wie this funktioniert in JavaScript. Das Problem ist, dass this hängt davon ab, wie die Funktion aufgerufen wird.

Erstens, wenn Sie die Funktion im Methodenstil aufrufen, this ist das, was man erwarten würde:

mySingleton.mouseHandler(); // this === mySingleton

Wenn Sie die Funktion an etwas anderes anhängen, funktioniert das auch.

var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton

Wenn Sie die Funktion deaktivieren, this wird zum Objekt des globalen Geltungsbereichs ( window )

var foo = mySingleton.mouseHandler;
foo(); // this === window

Und schließlich können Sie erzwingen, dass this etwas anderes zu sein mit call o apply :

var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy

Die Schlussfolgerung ist, dass this wird zur Laufzeit auf der Grundlage des Kontexts, in dem die Funktion aufgerufen wurde, bestimmt. Oftmals abstrahieren Frameworks, die es erlauben, "Klassen" zu erstellen, diese Details von Ihnen, indem sie implizit das Bindungsmuster in Ihrem Namen anwenden. Das ist der Grund, warum es früher funktionierte und jetzt nicht mehr.

Wie bereits von anderen erwähnt, können Sie Ihren Handler so ändern, dass er auf die Variable über ihren scoped name ( mySingleton ) oder wie besprochen anderweitig binden.

Hier ist ein Artikel, den ich vor ein paar Jahren zu diesem Thema geschrieben habe und der ausführlicher ist: http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword

Ich hoffe, das hilft!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X