11 Stimmen

Warum gibt der Zuweisungsoperator einen Wert und nicht eine Referenz zurück?

Ich habe gesehen, dass das folgende Beispiel dies erklärt Website und dachte, beide Antworten wären 20 und nicht die 10, die zurückgegeben wird. Er schrieb, dass sowohl das Komma als auch die Zuweisung einen Wert und keine Referenz zurückgeben. Ich verstehe nicht ganz, was das bedeutet.

Ich verstehe es in Bezug auf die Übergabe von Variablen in Funktionen oder Methoden, d.h. primitive Typen werden als Wert und Objekte als Referenz übergeben, aber ich bin nicht sicher, wie es in diesem Fall gilt.

Ich verstehe auch über den Kontext und den Wert von 'this' (nach Hilfe von Stackoverflow), aber ich dachte, in beiden Fällen würde ich immer noch es als eine Methode, foo.bar() aufrufen, was bedeuten würde, dass foo der Kontext ist, aber es scheint, dass beide zu einem Funktionsaufruf bar() führen.

Warum ist das so und was bedeutet das alles?

var x = 10;
var foo = {
  x: 20,
  bar: function () {return this.x;}
};

(foo.bar = foo.bar)();//returns 10
(foo.bar, foo.bar)();//returns 10

1 Stimmen

(foo.bar = foo.bar)() es also kommt auf meine Liste der Interviewfragen! ^.^

0 Stimmen

@Ben: Nicht (foo.bar, foo.bar)() ? ;-) Ich meine, wenn Sie esoterisch wollen...

11 Stimmen

@Ben - Lassen Sie mich wissen, für wen Sie arbeiten, damit ich nicht den Fehler mache, zu einem Vorstellungsgespräch zu kommen:)

12voto

T.J. Crowder Punkte 948310

Das hat nichts mit Werten oder Referenzen zu tun, sondern mit this Werte (wie Sie vermutet haben). In JavaScript, this eingestellt ist vollständig wie eine Funktion aufgerufen wird, nicht wo sie definiert ist. Sie setzen die this Wert auf eine von drei Arten:

  1. Rufen Sie die Funktion über eine Objekteigenschaft auf, indem Sie die Notation für Eigenschaftszugriffe verwenden, entweder in punktierter Notation ( obj.foo() ) oder die Klammerschreibweise ( obj["foo"]() ).
  2. Rufen Sie die Funktion über eine Objekteigenschaft mit einer with Anweisung (eigentlich nur eine Variante von Nr. 1, aber es lohnt sich, sie gesondert zu erwähnen, zumal sie aus dem Quellcode nicht ersichtlich ist)
  3. を使用します。 apply o call Eigenschaften der Funktionsinstanz.

In Ihren obigen Beispielen tun Sie nichts von alledem, so dass Sie die Funktion am Ende mit dem Standard this Wert, das globale Objekt, und so x kommt von dort und nicht von Ihrem foo Objekt. Hier ist eine andere Möglichkeit, darüber nachzudenken, was dieser Code tut:

var f = foo.bar; // Not calling it, getting a reference to it
f();             // Calls the function with `this` referencing the global object

Wenn Sie nicht direkt eine Eigenschaft verwenden, um den Anruf zu tätigen (stattdessen rufen Sie die Wert des Grundstücks und dann den Anruf damit tätigen), wird die this Handhabung nicht in Kraft tritt.

1 Stimmen

Es hat mit dem Referenztyp zu tun, denn sowohl der Komma-Operator als auch die einfache Zuweisung verwenden intern GetValue und dies führt dazu, dass die Basisobjekt der Referenz verloren gehen.

0 Stimmen

@CMS: Richtig, das ist der Mechanismus, der dem von mir beschriebenen Verhalten zugrunde liegt. Ich dachte nicht, dass es nötig wäre, in diese Tiefen vorzudringen. (Ich habe auch nicht über die Methode [[Call]] oder die Eigenschaft [[Scope]] gesprochen :-) ).

0 Stimmen

@TJ & CMS - Sie können so technisch antworten, wie Sie wollen. Wenn ich es nicht verstehe, werde ich Ihnen Fragen stellen. Auch die Antwort von CMS ergibt für mich mehr Sinn, da ich immer noch nicht verstehe, warum (foo.bar = foo.bar)() nicht mit foo.bar() gleichzusetzen ist. In deinem Beispiel hast du nur f = foo.bar verwendet und das macht für mich Sinn, da f() ein Funktionsaufruf ist, aber wenn es f.bar = foo.bar wäre, dann wäre das für mich gleichbedeutend mit f.bar(), was bedeutet, dass f 'dies' wäre

8voto

Christian C. Salvadó Punkte 763569

Sie sollten verstehen, wie der interne Referenztyp の作品です。

<strong>Nota: </strong>Dabei handelt es sich nicht um einen Datentyp der Sprache, sondern um einen internen Mechanismus zur Behandlung von Referenzen.

Eine Referenz besteht aus zwei Elementen, dem Basisobjekt y un Eigenschaftsname .

In Ihrem Beispiel ist die foo.bar Referenz sieht so aus.

// pseudo-code
foo.bar = {
  baseObject: foo,
  propertyName: 'bar'
}

Beide, die Komma-Operator y un einfache Zuweisung auf den Wert des Eigenschaftsnamens angewiesen sind, geht das Basisobjekt verloren, da nur ein einziger Wert zurückgegeben wird (dies geschieht durch die interne GetValue Betrieb).

So wird die interne GetValue Operation funktioniert:

// pseudo-code
GetValue(V) :
  if (Type(V) != Reference) return V;

  baseObject = GetBase(V); // in your example foo
  if (baseObject === null) throw ReferenceError;

  return baseObject.[[Get]](GetPropertyName(V)); 
  // equivalent to baseObject[v.PropertyName];

Wie Sie sehen, ist ein Wert zurückgegeben, so dass der ursprüngliche Verweis verloren geht.

Bearbeiten: Der Schlüssel zum Verständnis der Gründe (foo.bar = foo.bar)(); ist nicht gleichbedeutend mit foo.bar(); stützt sich im Einfache Zuweisung Operator, lassen Sie uns den Algorithmus sehen:

11.13.1 Simple Assignment (`=`) The production `AssignmentExpression` : `LeftHandSideExpression` = `AssignmentExpression`

is evaluated as follows:

  1. Evaluate LeftHandSideExpression.

  2. Evaluate AssignmentExpression.

3.Call GetValue(Result(2)).

4.Call PutValue(Result(1), Result(3)).

5.Return Result(3).

Grundsätzlich gilt, wenn Sie (foo.bar = foo.bar) die eigentliche Zuordnung ( Schritt 4. ) hat keine Auswirkungen, weil PutValue erhält nur den Wert des Verweises und setzt ihn mit demselben Basisobjekt zurück.

Der Schlüssel ist, dass der Zuweisungsoperator ( Schritt 5 ) der Wert, der in der Schritt 3 und wie ich bereits in der GetValue Pseudocode, gibt diese interne Methode eine Wert die nicht wirklich eine Basisobjekt .

0 Stimmen

Das wird mir eine Lehre sein. Ich habe zu T.J. Crowder gesagt, mach es so technisch wie du willst, aber jetzt kann ich es nicht verstehen. Ich werde über das, was Sie geschrieben haben, nachdenken und auf Sie zurückkommen, wenn das in Ordnung ist.

0 Stimmen

@Nick, klar, denk mal drüber nach, wir haben es hier mit einem völlig abstrakten Typ zu tun, der in der Spezifikation nur dazu da ist aussagekräftige Zwecke . Das kann es ein bisschen schwierig machen, nach Luft zu schnappen. Lesen Sie darüber und über die GetBase , GetPropertyName et GetValue internen Methoden, dann können wir einen Schritt weiter gehen und ich kann Ihnen erklären, wie der Referenztyp verwendet wird, um die this Wert, wenn Sie einen Funktionsaufruf tätigen.

0 Stimmen

Danke, Mann. Dein Verständnis von JavaScript ist unglaublich. Ich werde das, was du gesagt hast, mitnehmen und versuchen, es zu verstehen, auch mit etwas Recherche. An diesem Punkt muss ich die richtige Antwort vergeben, die ich TJ geben werde, nicht weil seine Antwort besser war, da alle Antworten großartig waren, sondern weil ich eine auswählen muss. Ich werde Sie und die anderen mit +1 bewerten, da ich sie alle für gute und hilfreiche Antworten halte. Nochmals vielen Dank für eure Hilfe.

2voto

SLaks Punkte 832502

Das ist ein Missverständnis.

Beide Beispiele geben die window 's x Eigenschaft, da sie nicht direkt aufgerufen werden auf foo .

Der Wert der this Schlüsselwort innerhalb einer Funktion hängt vom Kontext ab, in dem die Funktion aufgerufen wurde.

Bei einem gewöhnlichen Funktionsaufruf (z. B, myFunc() ), this wird das globale Objekt sein, das normalerweise window .
In einem Objektmethodenaufruf (z.B., foo.bar() ), this ist das Objekt, mit dem die Funktion aufgerufen wurde. (in diesem Fall, foo )

Sie können den Kontext explizit setzen, indem Sie myFunc.call(context, arg1, arg2) o myFunc.apply(context, argArray) .

Ihre beiden Beispiele sind normale Aufrufe eines Ausdrucks, der zu foo.bar .
Deshalb, this ist die window .

Sie sind gleichbedeutend mit

var func = (some expression);
func();

0 Stimmen

Ich verstehe alles, was Sie geschrieben haben, einschließlich Ihres Beispiels, aber in meinem Kopf entspricht (foo.bar = foo.bar) immer noch foo.bar() und nicht bar(), wie Sie mir sagen (was, wie ich weiß, korrekt ist). Sowohl Sie als auch CMS und TJ haben mir gute Antworten gegeben, aber ich muss mir das erst einmal zusammenreimen, denn ich bin ein bisschen einfältig.

2voto

Ben Blank Punkte 52357

Es kann hilfreich sein, sich den Punkt-Operator so vorzustellen, dass er sich ähnlich verhält wie ein with Erklärung. Wenn Sie die foo.bar() Das Ergebnis ist dasselbe, als wenn Sie laufen würden:

with (foo) {
    bar(); // returns 20
}

Dadurch wird Ihr bar Funktion mit foo das globale Objekt überlagert, so dass es in der Lage ist, die x sur foo und geben somit 20.

Wenn Sie nicht anrufen bar jedoch sofort, wie in (foo.bar = foo.bar) erhalten Sie stattdessen:

with (foo) {
    bar; // returns "bar" itself
}

Das Ergebnis wird dann aus den Klammern herausgeführt, was zu einer Zwischenanweisung wie <reference to bar>() die keinen Punktoperator hat, also keine with Anweisung, also kein Zugriff auf foo nur auf den Gesamtwert von x .

(Der Punkt-Operator hat keine eigentlich umwandeln in eine with Anweisung, aber das Verhalten ist ähnlich.)

0 Stimmen

Danke Mann - das hilft mir sehr. Ich hatte die with-Anweisung vorher noch nie benutzt, aber sie gibt mir etwas zum Nachforschen. Also in diesem Beispiel (nick.foo.low.man = foo.man.tan.bar)() wäre es das gleiche wie mit (foo.man.tan) { bar; // gibt "bar" selbst zurück }

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