var self = das?

182

Durch die Verwendung von Instanzmethoden als Rückruf für Ereignishandler wird der Bereich thisvon "Meine Instanz" in "Wie auch immer der Rückruf genannt wird" geändert . Mein Code sieht also so aus

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

Es funktioniert, aber ist das der beste Weg, es zu tun? Es sieht seltsam für mich aus.

defnull
quelle
15
Sie sollten dies vermeiden, selfda es ein window.selfObjekt gibt , das Sie möglicherweise versehentlich verwenden, wenn Sie vergessen, Ihre eigene Variable zu deklarieren self(z. B. wenn Sie Code verschieben). Das Erkennen / Debuggen kann ärgerlich sein. Besser so etwas zu benutzen _this.
Alastair Maw
3
Aus dem JavaScript-Tutorial : "Der Wert von thisist in JavaScript dynamisch. Er wird bestimmt, wenn eine Funktion aufgerufen wird , nicht, wenn sie deklariert wird."
DavidRR
Die Sache ist im globalen Bereich self === this. Daher ist selfin lokalen Kontexten sinnvoll und folgt dem Muster.
Programmierer5000

Antworten:

210

Diese Frage ist nicht spezifisch für jQuery, sondern allgemein für JavaScript. Das Kernproblem besteht darin, wie eine Variable in eingebetteten Funktionen "kanalisiert" wird. Dies ist das Beispiel:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

Diese Technik beruht auf der Verwendung eines Verschlusses. Es funktioniert jedoch nicht, thisda thises sich um eine Pseudovariable handelt, die sich dynamisch von Bereich zu Bereich ändern kann:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

Was können wir tun? Weisen Sie es einer Variablen zu und verwenden Sie es über den Alias:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thisist in dieser Hinsicht nicht eindeutig: argumentsist die andere Pseudovariable, die auf die gleiche Weise behandelt werden sollte - durch Aliasing.

Eugene Lazutkin
quelle
7
Sie sollten "dies" wirklich an den Funktionsaufruf binden und ihn nicht weitergeben oder damit variable Bereiche durchlaufen
Michael
10
@ Michael Warum ist das so?
Chris Anderson
@michael Sie arbeiten möglicherweise in Pipe, Map, Abonnements ... in einer Funktion und rufen keine Funktionen auf. In diesem Fall ändert sich "this", sodass der variable Bereich die Lösung ist
Skander Jenhani,
@ SkanderJenhani dieser Kommentar ist 8 Jahre alt. Meine Antwort wäre heute ganz anders
Michael
18

Ja, dies scheint ein allgemeiner Standard zu sein. Einige Programmierer benutzen sich selbst, andere benutzen mich. Es wird als Referenz zurück auf das "echte" Objekt im Gegensatz zum Ereignis verwendet.

Es hat etwas gedauert, bis ich es wirklich verstanden habe. Zuerst sieht es seltsam aus.

Normalerweise mache ich das ganz oben auf meinem Objekt (entschuldigen Sie meinen Demo-Code - er ist konzeptioneller als alles andere und keine Lektion in exzellenter Codiertechnik):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}
BenAlabaster
quelle
12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

edit: Trotzdem übernehmen verschachtelte Funktionen innerhalb eines Objekts eher das globale Fensterobjekt als das umgebende Objekt.

serkan
quelle
Sie scheinen zu beschreiben, was var self = thisfunktioniert, aber das ist nicht das, was die Frage stellt (die Frage lautet, ob dies die beste Lösung für das Problem ist).
Quentin
3
"Das Kernproblem besteht darin, eine Variable in eingebetteten Funktionen zu" kanalisieren "." Ich stimme dieser Aussage in der akzeptierten Antwort zu. Abgesehen davon ist an "dieser" Praxis nichts auszusetzen. Es ist ziemlich häufig. Als Antwort auf den Teil "Code sieht für mich seltsam aus" habe ich mich entschieden zu erklären, wie es funktioniert. Ich glaube, der Grund, warum der "Code für mich seltsam aussieht", liegt in der Verwirrung darüber, wie er funktioniert.
Serkan
12

Wenn Sie ES2015 oder Typenskript und ES5 ausführen, können Sie Pfeilfunktionen in Ihrem Code verwenden, und dieser Fehler tritt nicht auf. Dies bezieht sich auf den gewünschten Bereich in Ihrer Instanz.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});
Aran Dekar
quelle
5
Zur Erklärung: In ES2015 erfassen Pfeilfunktionen thisaus ihrem definierenden Bereich. Normale Funktionsdefinitionen machen das nicht.
defnull
@defnull Ich glaube nicht, dass es etwas Besonderes ist. Funktionen erstellen einen Blockbereich. Wenn Sie also anstelle einer Funktion die Pfeilnotation verwenden, können Sie keinen Bereich erstellen. Dies bezieht sich also immer noch auf die äußere Variable dieser.
Nimeshka Srimal
4

Eine Lösung hierfür besteht darin, Ihren gesamten Rückruf mit der bindMethode von Javascript an Ihr Objekt zu binden .

Sie können dies mit einer benannten Methode tun.

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

Oder mit einem anonymen Rückruf

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Wenn Sie dies tun, anstatt auf var self = thisetwas zurückzugreifen, verstehen Sie, wie sich die Bindung von thisin Javascript verhält und nicht auf eine Abschlussreferenz angewiesen ist.

Außerdem ist der Fettpfeiloperator in ES6 im Grunde derselbe wie ein Aufruf .bind(this)einer anonymen Funktion:

doCallback( () => {
  // You can reference "this" here now
});
Bart Dorsey
quelle
Persönlich denke ich, dass .bind (this) nur mehr tippt, wenn ich diesen Verweis auf mich (oder was auch immer) ändere und ihn dann einfach benutze, wird der Code sauberer. Fetter Pfeil ist die Zukunft tho.
Davidbuttar
3

Ich habe jQuery nicht verwendet, aber in einer Bibliothek wie Prototype können Sie Funktionen an einen bestimmten Bereich binden. In diesem Sinne würde Ihr Code folgendermaßen aussehen:

 $('#foobar').ready('click', this.doSomething.bind(this));

Die bind-Methode gibt eine neue Funktion zurück, die die ursprüngliche Methode mit dem von Ihnen angegebenen Bereich aufruft.

neonski
quelle
Analog zu: $('#foobar').ready('click', this.doSomething.call(this));richtig?
Muers
Die bindMethode funktioniert in älteren Browsern nicht. Verwenden Sie $.proxystattdessen die Methode.
Guffa
2

Fügen Sie einfach hinzu, dass Sie dies in ES6 aufgrund von Pfeilfunktionen nicht tun müssen, da diese den thisWert erfassen .

aug
quelle
1

Ich denke, es hängt tatsächlich davon ab, was Sie in Ihrer doSomethingFunktion tun werden. Wenn Sie MyObjectmit diesem Schlüsselwort auf Eigenschaften zugreifen möchten , müssen Sie dies verwenden. Ich denke aber, dass das folgende Codefragment auch funktioniert, wenn Sie keine besonderen Dinge mit object(MyObject)Eigenschaften tun .

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

});
Scott Harrison
quelle