Ist var self = this; ein schlechtes Muster?

77

Ich brauche:

var self = this;

viel in meinen Javascript 'Klassen'. Obwohl dies üblicherweise gemacht wird, fühlt es sich ein bisschen falsch an. Was ich in dieser Frage zu finden hoffe, ist ein besserer Weg, damit umzugehen, oder etwas, das mich davon überzeugt, dass dies in Ordnung ist.

Ist dies die Standardmethode, um die richtigen Bindungen beizubehalten? Sollte ich standardisieren, dass ich überall "Selbst" verwende, es sei denn, ich brauche ausdrücklich "Dies".

edit : Ich weiß genau, warum ich das brauche, ich frage mich nur, ob es ein bisschen böse ist und warum. Mir ist bekannt, dass es auch die integrierte Javascript-Funktion 'apply' gibt, mit der der Bereich beim Aufrufen einer Methode explizit definiert wird. Ist es besser?

Evert
quelle
2
Ich benutze immer var that = this, ich habe mich ehrlich gesagt nicht einmal darum gekümmert, mich zu bewerben / anzurufen, aber jetzt habe ich über diese Methoden gelesen, danke für diese Frage!
Anders
Ich persönlich denke, dass dies an sich nicht schlecht ist, sondern ein Zeichen dafür, dass Sie Ihr Design ein wenig optimieren könnten.
Dmitry

Antworten:

50

Wie andere gesagt haben: Diese "zusätzliche Variable" ist (auf einer bestimmten Ebene) der einzige Weg, um die Tatsache zu umgehen, dass thises sich um einen speziellen Ausdruck handelt und daher, da sie keine Variable ist, nicht an einen Ausführungskontext / -abschluss gebunden ist.

Ich denke jedoch, dass Sie fragen (oder was ich wirklich beantworten möchte):

Sollte man var self = thisbei jeder Methode / jedem Konstruktor an die Spitze setzen ?

Zusammenfassung

Obwohl ich dies einmal versucht habe und dieselbe Frage hatte, verwende ich diesen Ansatz nicht mehr. Jetzt reserviere ich das Konstrukt für den Fall, dass ich Zugriff in einem Verschluss benötige. Für mich fügt es ein wenig hinzu "Hey, das ist was ich wirklich will!" semantisch zu meinem Code:

this -> this und self -> this (but really that) in a closure

Fragen à la carte:

... Obwohl dies üblicherweise gemacht wird, fühlt es sich ein bisschen falsch an. Was ich in dieser Frage zu finden hoffe, ist ein besserer Weg, damit umzugehen, oder etwas, das mich davon überzeugt, dass dies in Ordnung ist.

Tu, was sich für dich richtig anfühlt. Haben Sie keine Angst, eine Methode auszuprobieren und später zurückzuschalten (aber versuchen Sie bitte, in jedem Projekt konsistent zu bleiben :-)

Ist dies die Standardmethode, um die richtigen Bindungen beizubehalten? Sollte ich standardisieren, dass ich überall "Selbst" verwende, es sei denn, ich brauche ausdrücklich "Dies".

"self" ist der am häufigsten verwendete Name. Wie oben, bevorzuge ich den umgekehrten Ansatz - thisaußer wenn eine Verschlussbindung erforderlich ist.

..wenn es ein bisschen böse ist und warum.

Das Böse ist ein dummer subjektiver Begriff (wenn auch manchmal lustig). Ich habe nie gesagt, dass es böse ist, nur warum ich dem Ansatz nicht folge. Einige Leute sagen mir, ich sei "böse", weil ich keine Semikolons benutze. Ich sage ihnen, sie sollten sich eigentlich gute Argumente einfallen lassen und / oder JavaScript besser lernen :-)

Mir ist bekannt, dass es auch die integrierte Javascript-Funktion 'apply' gibt, mit der der Bereich beim Aufrufen einer Methode explizit definiert wird. Ist es besser?

Das Problem dabei apply/callist, dass Sie sie am Punkt des Funktionsaufrufs verwenden müssen. Es hilft nicht, wenn jemand anderes eine Ihrer Methoden aufruft, da die thismöglicherweise bereits deaktiviert ist. Dies ist am nützlichsten, wenn Sie beispielsweise Rückrufe im jQuery-Stil ausführen, bei denen thisdas Element / Element des Rückrufs usw. ist.

Nebenbei...

Ich möchte vermeiden, dass Mitglieder sich selbst brauchen, und daher generell alle Mitgliederfunktionen auf Eigenschaften übertragen, bei denen der Empfänger (this ) nur "durchfließt", was normalerweise "wie erwartet" ist.

Die "privaten" Methoden in meinem Code beginnen mit einem "_" und wenn der Benutzer sie aufruft, ist das auf ihnen. Dies funktioniert auch besser (ist wirklich erforderlich), wenn der Prototyp-Ansatz für die Objekterstellung verwendet wird. Allerdings Douglas Crockford nicht einverstanden mit diesem „privaten“ Ansatz von mir und es gibt einige Fälle , in denen die Look-up - Kette Sie durch Injizieren eines unerwarteten Empfänger vereiteln kann:

Die Verwendung des im Konstruktor gebundenen "Selbst" sperrt auch die Obergrenze der Suchkette für eine Methode (sie ist nicht mehr nach oben polymorph!), Die möglicherweise korrekt ist oder nicht. Ich denke es ist normalerweise falsch.

Viel Spaß beim Codieren.


quelle
1
Gute Antwort. Könnten Sie diese Aussage näher erläutern: "Ich möchte vermeiden, dass Mitglieder" sich selbst brauchen "und daher im Allgemeinen alle Mitgliederfunktionen auf Eigenschaften fördern, bei denen der Empfänger (dies) nur" durchfließt ", was normalerweise" wie erwartet "ist." Wie machst Du das?
Evert
1
@Evert Wenn jede Methode Mitglied des Objekts ist, wird / sollte sie aufgerufen werden, da eine Form obj.membernormalerweise sicherstellt, dass die Methode thiskorrekt ist (seit obj->this(obj)->this(obj)->...). Wenn Sie den Crockford-Link im Beitrag sehen, werden Sie sehen, dass der Ansatz der privaten Methode das Muster bricht, da die privaten "Methoden" jetzt in Variablen im Konstruktor gespeichert und somit nicht im obj.memberFormular aufgerufen werden. Dies wird thisin ihnen abgeworfen (wie thises objin den obigen Beispielen lediglich der Empfänger der Funktion zum Zeitpunkt des Aufrufs ist ), sofern keine zusätzliche Arbeit geleistet wird.
3
aelfist das Fensterobjekt - ich würde es vermeiden, reservierte Wörter zu verwenden, und var that = thisodervar _self = this
chovy
@chovy Interessanter Punkt, an den ich nie gedacht habe. Ich denke, ich vermeide das Problem immer, indem ich immer [außer in den seltenen window.xwindow.self
Sei doppelt so böse. Fügen Sie var self = falseden globalen Kontext ein und fügen Sie dort var self = this;Methoden hinzu, wo Sie sie benötigen. Dann, wenn Sie jemals Zweifel haben, schreiben Sie (self || this).method(...). Es ist eine schreckliche Codierung, aber es fühlt sich gut an.
Orwellophile
17

Ja, das ist der Standardweg.

Function.apply() und Function.call() kann helfen, aber nicht immer.

Folgendes berücksichtigen

function foo()
{
  var self = this;
  this.name = 'foo';

  setTimeout( function()
  {
    alert( "Hi from " + self.name );
  }, 1000 );       
}

new foo();

Wenn Sie dies tun wollten, aber die Verwendung einer Variablen wie selfund verwenden call()oder apply()stattdessen ... nun ... vermeiden möchten , schauen Sie sie sich an und beginnen zu versuchen, stellen jedoch bald fest, dass Sie dies einfach nicht können. setTimeout()ist für den Aufruf des Lambda verantwortlich, sodass Sie diese alternativen Aufrufstile nicht nutzen können. Am Ende würden Sie immer noch eine Zwischenvariable erstellen, die einen Verweis auf das Objekt enthält.

Peter Bailey
quelle
4
Tut mir leid, wenn ich falsch verstanden habe, aber es funktioniert, wenn Sie dies tun: setTimeout( (function() { alert( "Hi from " + this.name ); }).apply(this), 1000 ); }
Drodsou
@drodsou Nein, dein Code funktioniert nicht, sieh dir diese JSBin an . Es schlägt mit einem Syntaxfehler fehl, da Sie in JS nicht einfach etwas in "()" einschließen können. Wie Sie sehen können, gibt window.setTimeout keine Funktion / kein Objekt zurück, sondern ein Grundelement vom Typ "number", das die ID des Zählers darstellt.
Sentenza
@Peter Bailey gibt es eigentlich einen anderen Weg , ich sage aber nicht, dass das eine bessere Lösung ist.
Sentenza
3
@drodsou Wenn Sie apply (this) verwenden, wird die Funktion sofort ausgeführt, sodass undefined als erstes Argument von setTimeout zurückgegeben wird, unabhängig davon, ob es eine Bindungsfunktion gibt: window.setTimeout (function () {alert ("Hi from" + this.name);} .bind (this), 1e3);
AlexanderB
1
Also ... so wie ich das sehe: Wenn Sie bind verwenden, erhalten Sie einen KURZEREN Code als den in dieser Antwort angegebenen :). Diese Antwort ist also falsch. Das "aber nicht immer" ist irreführend.
Adam Skobodzinski
9

Ist dies die Standardmethode, um die richtigen Bindungen beizubehalten?

Es gibt keinen Standard für JavaScript und Klassen- / Instanzsysteme. Sie müssen auswählen, welche Art von Objektmodell Sie bevorzugen. Hier ist ein weiterer Link zu einem Hintergrund. Fazit: Es gibt keine Schlussfolgerung.

In der Regel geht das Speichern einer Kopie var self= this;(*) in einem Abschluss mit einem Objektmodell einher, das um Verschlüsse mit Kopien jeder Methode pro Instanz erstellt wird. Das ist eine gültige Art, Dinge zu tun. ein bisschen weniger effizient, aber auch in der Regel etwas weniger Arbeit als die Alternative, ein Objektmodell , Prototyping gebaut um, unter Verwendung this, apply()und ECMAScript Fifth Edition bind()gebundene Methoden zu erhalten.

Was mehr als "böse" gezählt werden könnte, ist, wenn Sie einen Mischmasch beider Stile im selben Code haben. Leider tun dies viele gängige JS-Codes (weil wir ehrlich sind, versteht niemand das bizarre native Objektmodell von JavaScript wirklich ).

(*: Ich verwende normalerweise thatanstelle von self; Sie können einen beliebigen Variablennamen verwenden, haben aber selfbereits eine etwas dunkle und völlig sinnlose Bedeutung als windowMitglied, das auf das Fenster selbst zeigt.)

Bobince
quelle
7

Ich bin gerade auf diese Frage gestoßen, weil meine Mitarbeiter süchtig nach sich selbst / diesen Variablen waren und ich verstehen wollte, warum ...

Ich denke , dass es eine bessere Art und Weise ist, zu beschäftigen diese in nowdays:

function () {}.bind(this);      // native
_.bind(function () {}, this);   // lodash
$.proxy(function () {}, this);  // jquery
AlexanderB
quelle
4

In Javascript und anderen Sprachen mit Verschlüssen kann dies sehr wichtig sein. Das Objekt, thisauf das in einer Methode verwiesen wird, kann sich tatsächlich ändern . Sobald Sie Ihre selfVariable gleich gesetzt haben this, bleibt self zuverlässig eine Referenz auf das betreffende Objekt, auch wenn thisspäter auf etwas anderes verwiesen wird .

Dies ist ein wichtiger Unterschied zwischen Javascript und vielen anderen Sprachen, in denen wir arbeiten. Ich komme aus .Net, daher kam mir diese Art von Dingen zunächst auch seltsam vor.

Edit : Ah, okay, du weißt das alles. (Vielleicht immer noch hilfreich für jemand anderen.) Ich füge hinzu, dass Übernehmen (und Aufrufen) eher für die Verwendung von "von außen" geeignet sind und einer Funktion, die Sie aufrufen, einen bestimmten Bereich geben, den Sie bereits kennen. Sobald Sie sich in einer Funktion befinden und sich weiter unten in Verschlüssen befinden, wird die folgende Technik angewendet:

  var self = this;

ist der geeignetere Weg ( einfach und klar ), um Ihren aktuellen Bereich zu verankern .

Patrick Karcher
quelle
2

Dies geschieht höchstwahrscheinlich, um einen Verweis darauf zu erhalten, thiswann sich der Geltungsbereich ändern wird (im Falle einer Schließung). Ich weiß nicht, dass ich es für eine schlechte Praxis oder ein schlechtes Muster an und für sich halten würde, nein. Sie sehen ähnliche Dinge häufig bei Bibliotheken wie jQuery und viel bei der Arbeit mit AJAX.

gddc
quelle
1

Ich denke, es gibt ein Argument dafür, immer var self = thisin jede Methode einbezogen zu werden : menschliche Faktoren.

Es wird oft genug benötigt, damit Sie eine Mischung aus Methoden haben, auf die zugegriffen wird, thisund andere, die sie verwendenself für denselben Zweck verwenden. Wenn Sie Code von einem zum anderen verschieben, werden plötzlich eine Reihe von Fehlern angezeigt.

Gleichzeitig ertappe ich mich dabei self.foo, aus Gewohnheit abwesend zu schreiben , wenn ich keine benötigt oder hinzugefügt habe var self = this. Ich denke, es könnte Sinn machen, es sich zur Gewohnheit zu machen, es immer einzubeziehen, ob gebraucht oder nicht.

Das einzige Problem ist ... this, selfoder thatalle sind hässliche Pocken in Ihrem Code und ich hasse sie alle. Also ich denke , dass es besser ist , mit delegierten Methoden zu vermeiden , wo möglich , so dass Sie mit vermeiden können this, thatoder selfdie überwiegende Mehrheit der Zeit, und die Verwendung , .bind(this)wenn Sie sonst greifen könnte self/ that. Es ist sehr selten, dass Sie durch die Verwendung von Delegaten für den Prototyp ohnehin eine erhebliche Menge an Speicherplatz sparen.

Ein netter Nebeneffekt dieses Ansatzes ist, dass Sie nicht allen privaten Variablen ein Präfix voranstellen müssen _, da diese wirklich privat sind und die öffentlichen Eigenschaften vom führenden this.Benutzer aufgerufen werden , wodurch Ihr Code besser lesbar wird.

Wie Bobince sagte, var that = thisist besser, weil es nicht schattiert window.self. self = thisklingt für mich weniger umständlich, aber manchmal erhalten Sie verwirrende Fehlermeldungen wie property myMethod not found on global, weil Sie die self = thisZeile vergessen haben .

John Starr Dewar
quelle
Könnten Sie bitte eine Idee mit Ihrer Idee, sich durch .bind (this) zu ersetzen, einbringen?
Sentenza
1
window.setTimeout (function () {this. $ btnAccept.tooltip ('show');} .bind (this), 256);
AlexanderB
1

Ich möchte nur darauf hinweisen, dass 'self' gleich 'window' ist. Versuchen Sie, window === self an die Konsole auszugeben. Sie sollten dieses Muster mit 'that' oder ähnlichem als Variablennamen verwenden. Vermeiden Sie die Verwendung von 'self', da es bereits vom Browser verwendet wird (ein Fehler, und Sie erstellen selbst eine globale Variable). Auch wenn es seltsam klingt, ist es besser, "das" für seinen Namen zu verwenden, da andere Entwickler sofort wissen, was Sie in Ihrem Code erreichen wollten. Vermeiden Sie die Verwendung nicht standardmäßiger Variablennamen. Ich glaube, dass dies ein wichtiger Hinweis ist, aber er wurde nur in einem Kommentar erwähnt, deshalb wollte ich ihn sichtbarer machen.

Goran Vasic
quelle
Versuchen Sie, eine globale Variable in einem Browser mithilfe des Selbstobjekts zu erstellen und eine Geige zu posten. In meinem Browser (Chromium) funktioniert dies nicht, da es eine Eigenschaft "self" in "window" definieren würde, wenn Sie "self = ..." ohne "var" verwenden würden.
Sentenza
Ich bin mir nicht sicher, warum Sie eine neue globale Variable namens "self" erstellen. In JavaScript können Sie zwar JavaScript selbst ändern, aber Sie sollten dabei äußerst vorsichtig sein, da andere Entwickler eine Standardversion von JavaScript erwarten, wenn Sie ihnen Ihren Code zur Verfügung stellen. Wenn Sie "sich selbst" etwas zuweisen, überschreiben Sie es tatsächlich. Ich würde das vermeiden. Ich habe nur gesagt, dass beim Vergleich von Standardfensterobjekt und Selbst standardmäßig dasselbe Objekt verwendet wird. Mit anderen Worten, "Selbst" und "Fenster" sind Synonyme
Goran Vasic
(Ein Fehler und Sie werden sich eine globale Variable erstellen)
Sentenza
1

Es ist 6 Jahre später und ich muss einige Dinge hinzufügen:

bind()ist jetzt häufig genug, um überall verwendet zu werden. Ich benutze es oft als Alternative. Manchmal fühlt es sich klarer an. Ich benutze immer noch gelegentlichvar self = this; . obwohl.

Pfeilfunktionen werden langsam nutzbar. Die Syntax ist etwas kürzer, was nett ist, aber ich denke, das Killer-Feature ist wirklich, dass sie standardmäßig immer an den übergeordneten Bereich gebunden sind.

Diese:

var self = this;
var foo = function(a) {

  return self.b + a;

};

Kann jetzt geschrieben werden als:

var foo = a => this.b + a;

Dies ist die "optimistischste" Verwendung von Pfeilfunktionen, aber sie ist ziemlich süß.

Und nur zum Schluss, es ist nichts falsch daran:

var self = this;
Evert
quelle
0

Ich mag das. Es ist "selbst" erklärend. Douglas Crockford hat dazu einige Dinge zu sagen. Er gibt an, dass die Verwendung von "das" Konvention ist. Sie können Crockford kostenlos sehen, wenn Sie zum Yui-Theater fahren und sich Videos über Javascript ansehen .

Phluks
quelle
1
Vielen Dank für dein Feedback. Ich glaube ich verstehe was du meinst. Ich glaube jedoch nicht, dass die Frage ohne Kontext beantwortet werden kann. Ich mag zufällig den Kontext, den Douglas
vorbringt