2010-06-15 8 views
11

Ho visto l'esempio di seguito spiegato su questo site e ho pensato che entrambe le risposte sarebbero 20 e non il 10 che viene restituito. Ha scritto che sia la virgola che l'assegnazione restituiscono un valore, non un riferimento. Non capisco cosa significhi.Perché l'operatore di assegnazione restituisce un valore e non un riferimento?

Lo capisco in relazione al passaggio di variabili in funzioni o metodi in cui i tipi primitivi vengono passati per valore e oggetti per riferimento, ma non sono sicuro di come si applica in questo caso.

Capisco anche il contesto e il valore di "this" (dopo l'aiuto di stackoverflow) ma ho pensato che in entrambi i casi lo avrei ancora invocato come metodo, foo.bar() che vorrebbe dire foo è il contesto ma sembra che entrambi risultino in una funzione call bar().

Perché è questo e cosa significa tutto questo?

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

'(foo.bar = foo.bar)()' è così ** ** andando sulla mia lista di domande di intervista! ^.^ –

+0

@ Ben: Non '(foo.bar, foo.bar)()'? ;-) Voglio dire, se vuoi esoterico ... –

+9

@ Ben - Fammi sapere per chi lavori, quindi non commetto l'errore di venire per un'intervista :) – screenm0nkey

risposta

11

Non ha a che fare con i valori vs. riferimenti, ha a che fare con i valori this (come si sospetta). In JavaScript, this è impostato interamente in base a come viene chiamata una funzione, non dove è definita. Si imposta il valore this in uno dei tre modi:

  1. Chiamare la funzione tramite proprietà di un oggetto utilizzando la proprietà di accesso notazione, sia notazione con punti (obj.foo()) o la notazione tra parentesi (obj["foo"]()).
  2. Chiamare la funzione tramite proprietà di un oggetto utilizzando un with dichiarazione (in realtà solo una variante di # 1, ma vale la pena chiamare separatamente, in particolare in quanto non è evidente dal codice sorgente)
  3. Utilizzare i apply o call caratteristiche del istanza di funzione.

Nei tuoi esempi di cui sopra, non stai facendo una di queste, così si finisce per chiamare la funzione con il valore predefinito this, l'oggetto globale, e così x viene da lì piuttosto che dal vostro foo oggetto. Ecco un altro modo di pensare a quello che il codice sta facendo:

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

Se non si utilizza direttamente una proprietà di fare effettivamente la chiamata (invece recuperare il valore della proprietà e quindi effettuare la chiamata con quella), la gestione di this non dà il via.

+1

Ha a che fare con il tipo di riferimento, dal momento che entrambi, l'operatore virgola e l'assegnazione semplice utilizzano internamente 'GetValue', e ciò fa sì che l'* oggetto di base * del riferimento venga perso. – CMS

+0

@CMS: Giusto, questo è il meccanismo sottostante al comportamento che ho descritto. Non pensavo che scavare in quelle profondità fosse necessario. (Non ho parlato del metodo [[Call]] o della proprietà [[Scope]], o :-)). –

+0

@TJ & CMS: sentiti libero di rispondere tecnicamente come preferisci. Se non lo capisco, ti farò delle domande. Anche la risposta di CMS ha più senso per me perché ancora non riesco a capire perché (foo.bar = foo.bar)() non equivale a foo.bar(). Nel tuo esempio hai usato solo f = pippo.bar e questo ha senso per me dato che f() è una chiamata di funzione ma se f.bar = pippo.bar quindi a me equivale a f.bar() che significa f sarebbe "questo" – screenm0nkey

2

Lo stai fraintendendo.

Entrambi gli esempi stanno tornando x proprietà s' il window, dal momento che non vengono direttamente invocati su foo.

Il valore della parola chiave this all'interno di una funzione dipende dal contesto in cui è stata richiamata la funzione.

In una normale chiamata di funzione (ad esempio, myFunc()), this sarà l'oggetto globale, che di solito è window.
In una chiamata al metodo oggetto (ad esempio, foo.bar()), this sarà l'oggetto su cui è stata richiamata la funzione. (in questo caso, foo)

È possibile impostare il contesto in modo esplicito chiamando myFunc.call(context, arg1, arg2) o myFunc.apply(context, argArray).

Entrambi gli esempi sono invocazioni normali di un'espressione che restituisce foo.bar.
Pertanto, this è il window.

sono equivalenti a

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

Capisco tutto ciò che hai scritto incluso il tuo esempio ma nella mia testa (foo.bar = foo.bar) equivale ancora a foo.bar() e non bar() come mi stai dicendo (che so è corretto). Sia tu, CMS e TJ mi avete dato delle buone risposte, ma ho bisogno di capirlo perché sono un po 'semplice. – screenm0nkey

8

Dovresti capire come funziona l'interno Reference Type.

Nota: Questo non è un tipo di dati di lingua, è un meccanismo interno per gestire i riferimenti.

Un riferimento è composto da due elementi, l'oggetto di base e un nome struttura.

Nell'esempio, il riferimento foo.bar si presenta così.

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

Sia la comma operator e simple assignment, si basano su come ottenere il valore del nome della proprietà, che causa l'oggetto base da perdere, poiché un singolo valore viene restituito (questo avviene attraverso il GetValue funzionamento interno) .

Questo è come funziona il GetValue funzionamento interno:

// 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]; 

Come si vede, un valoreviene restituito, così si perde il riferimento originale.

Edit: la chiave per comprendere il motivo per cui (foo.bar = foo.bar)(); non è equivalente alla foo.bar(); si basa nel Simple Assignment operatore, vediamo l'algoritmo:

 
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). 

In pratica quando si effettua (foo.bar = foo.bar) l'assegnazione effettiva (Fase 4.) non ha effetto perché PutValue otterrà solo il valore del riferimento e lo riposizionerà, con lo stesso oggetto base.

La chiave è che l'operatore di assegnazione restituisce (Fase 5) il valore ottenuto nella Passaggio 3 e come detto prima in pseudo-codice GetValue, questo metodo interno restituisce un valoreche doesn' t avere un oggetto di base.

+0

Questo mi insegnerà. Ho detto a T.J. Crowder lo rende tecnico come vuoi ma ora non riesco a capirlo. Dovrò pensare a quello che hai scritto e tornare da te se è OK. – screenm0nkey

+0

@Nick, certo, pensaci, abbiamo a che fare con un tipo completamente astratto, che esiste nella specifica puramente per scopi * espositivi *. Questo può renderlo un po 'difficile da rantolare. Leggilo a proposito dei metodi interni di 'GetBase',' GetPropertyName' e 'GetValue', poi possiamo fare un ulteriore passo avanti e posso spiegarti come viene utilizzato il tipo Reference per impostare il valore' this' quando si esegue una funzione chiamata. – CMS

+0

Grazie amico. La tua comprensione di JavaScript è incredibile. Porterò via quello che hai detto e cercherò di capirlo, anche con qualche ricerca. A questo punto devo assegnare la risposta corretta che darò a TJ, non perché la sua risposta fosse migliore dato che tutte le risposte erano grandiose, ma perché dovevo sceglierne una. Farò +1 tu e gli altri mentre pensavo che fossimo tutte risposte buone e utili. Grazie ancora per il vostro aiuto. – screenm0nkey

2

Può essere utile pensare all'operatore punto come comportandosi in modo simile a un'istruzione with.Quando si esegue foo.bar(), il risultato è più o meno lo stesso come se è stato eseguito:

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

Questo farà eseguire la funzione bar con foo sovrapposto sopra l'oggetto globale, permettendo così di trovare il x in foo e quindi di ritorno 20.

Se non si chiama bar immediatamente, anche se, come in (foo.bar = foo.bar), voi invece ottiene:

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

Il risultato è quindi passato fuori dalle parentesi, producendo una istruzione intermedia come <reference to bar>(), che non ha un operatore punto, quindi nessuna istruzione with, quindi nessun accesso a foo, solo al valore globale di x.

(L'operatore punto non lo fa in realtà convertito ad una dichiarazione with, naturalmente, ma il comportamento è simile.)

+0

Grazie amico - Questo mi aiuta molto. Non avevo usato la frase con in precedenza, ma mi dava qualcosa da ricercare. Quindi in questo esempio (nick.foo.low.man = foo.man.tan.bar)() sarebbe lo stesso di con (foo.man.tan) { bar; // restituisce "bar" stesso } – screenm0nkey

Problemi correlati