15

Capisco che l'operatore di assegnazione abbia ragione associativo.Confusione di assegnazioni multiple

Così, per esempio x = y = z = 2 equivale a (x = (y = (z = 2)))

Stando così le cose, ho provato la seguente:

foo.x = foo = {a:1} 

mi aspettavo che l'oggetto foo verrebbe creato con valore {a:1} e quindi la proprietà x sarà creato il foo che sarà solo un riferimento all'oggetto foo.

(Questo è in realtà quello che succede se ero a separare l'istruzione di assegnazione multipla in due separati prospetti foo = {a:1};foo.x = foo;)

Il risultato è stato in realtà:

ReferenceError: foo is not defined(…)

Allora ho provato la seguente:

var foo = {}; 
foo.x = foo = {a:1}; 

Ora non ottengo più l'eccezione ma foo.x non è definito!

Perché il compito non funziona come mi aspettavo? Declinazione di responsabilità: la domanda "duplicata" sembra essere molto diversa da quella che sto chiedendo, poiché il problema è che le variabili create nell'assegnazione erano globali, come appunto alle variabili create con il var parola chiave. Questo non è il problema qui.

+0

a 'var foo;' è in ordine –

+0

Buona domanda. Anche dopo aver usato var foo, non vedo un puntello "x" nell'oggetto foo. Come succede? –

+0

@PaulRoub Non è un problema - ho aggiunto una dichiarazione di non responsabilità nella domanda con una breve spiegazione del perché – Danield

risposta

10

C'è una differenza importante fra associatività e ordine di valutazione.

In JavaScript, anche se l'operatore di assegnazione gruppi destra a sinistra, gli operandi sono valutate sinistra a destra prima di eseguire le assegnazioni effettive (che fanno verificano destra a sinistra).Considerate questo esempio:

var a = {}; 
var b = {}; 
var c = a; 

c.x = (function() { c = b; return 1; })(); 

La variabile c fa riferimento inizialmente a, ma il lato destro del compito imposta c-b. Quale proprietà viene assegnata, a.x o b.x? La risposta è a.x perché il lato sinistro viene valutato per primo, quando c fa ancora riferimento a a.

In generale, l'espressione x = y è valutata come segue:

  1. Valutare x e ricordare il risultato.
  2. Valuta e ricorda il risultato.
  3. Assegnare il risultato del passaggio 2 al risultato del passaggio 1 (e restituire il primo come risultato dell'espressione x = y).

Cosa succede con assegnazioni multiple, come in x = (y = z)? Ricorso!

  1. Valuta x e ricorda il risultato.
  2. Valuta y = z e ricorda il risultato. Per fare questo:
    1. valutare e ricordare il risultato.
    2. Valuta z e ricorda il risultato.
    3. Assegnare il risultato del passaggio 2.2 al risultato del passaggio 2.1 (e restituire il primo come risultato dell'espressione y = z).
  3. Assegnare il risultato del passaggio 2 al risultato del passaggio 1 (e restituire il primo come risultato dell'espressione x = (y = z)).

Ora diamo un'occhiata al vostro esempio, leggermente modificati:

var foo = {}; 
var bar = foo;   // save a reference to foo 
foo.x = (foo = {a:1}); // add parentheses for clarity 

foo.x viene valutata prima foo viene assegnato a {a:1}, quindi la proprietà x viene aggiunto al {} oggetto originale (che si può verificare, mediante esaminando bar).

+0

Grazie per questa grande spiegazione , ma continuo a non seguirlo completamente. Capisco che l'espressione a sinistra come 'cx' o' foo.x' sarà valutata * prima *, tuttavia, perché non è * successivamente assegnata * con il valore di ritorno della funzione (= 1) o con foo = {a: 1} nel mio esempio .... come tu stesso spieghi nel passaggio 3? – Danield

+0

'foo.x' ** è ** effettivamente impostato sul risultato di' foo = {a: 1} '. La domanda è, quale oggetto viene effettivamente aggiornato-'{}' o '{a: 1}'? Nei passi per la valutazione di 'x = (y = z)', sostituire 'foo.x' per' x', 'foo' per' y' e '{a: 1}' per 'z'. Al punto 1, 'pippo.x' è valutato; il risultato è la proprietà 'x' dell'oggetto' {} '. Nel passaggio 2.3, 'pippo' viene impostato su' {a: 1} '. Nel passo 3, il risultato del passo 2 (l'oggetto '{a: 1}) viene assegnato al risultato del passo 1 (la proprietà' x' dell'oggetto '{}'). Quindi il valore finale di 'foo' è' {a: 1} ', e il valore finale di' bar' è '{x: {a: 1}}'. –

+0

Ok, ma perché la proprietà 'x' dell'oggetto' {} 'non viene assegnata con l'oggetto' {a: 1} '.... non dovrebbe essere creata la proprietà' x'? o per dirla in modo un po 'diverso ... perché è necessario 'bar'? – Danield

1

curato la risposta a rendere semplice

Prima di tutto bisogna capire la differnce tra Reference e Value- tipo.

var foo = {}; 

foo variabile contiene un riferimento a un oggetto in memoria, consente di dire A

Ora, ci sono due arti di funzioni di accesso: Accessor variabile e proprietà accessor.

Così foo.x = foo = {a:1} possono essere intesi come

[foo_VARIABLE_ACCESSOR][x_PROPERTY_ACCESSOR] = [foo_VARIABLE_ACCESSOR] = {a:1} 

!!! La catena di accesso viene valutata per prima per ottenere l'ultimo accesso, che viene quindi valutato associativo.

A['x'] = foo = {a:1} 

Proprietà Accessor sono separati in setter e getter

var foo = { bar: {} }; 
foo.bar.x = foo = {a:1} 

Qui dove sono decared due oggetti nidificati foo e bar.In memoria abbiamo quindi due oggetti A e B.

[foo_VAR_ACCESSOR][bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1} 

> A[bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1} 
> B[x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1} 
> B['x'] = foo = {a: 1} 

Qui avete

var A = {}; 
var B = {} 
Object.defineProperty(A, 'bar', { 
    get() { 
     console.log('A.bar::getter') 
     return B; 
    } 
}) 
Object.defineProperty(B, 'x', { 
    set() { 
     console.log('B.x::getter') 
    } 
}); 

var foo = A; 
foo.bar.x = foo = (console.log('test'), 'hello'); 

// > A.bar.getter 
// > test 
// > B.x.setter 
0

Grande questione piccolo esempio. La cosa da ricordare qui è che JavaScript usa i puntatori per tutto. È facile dimenticarlo poiché è impossibile accedere ai valori che rappresentano gli indirizzi di memoria in JavaScript (vedere this SO question). Ma rendersene conto è molto importante per capire molte cose in JavaScript.

Quindi la dichiarazione

var foo = {}; 

crea un oggetto nella memoria e assegna un puntatore a quell'oggetto a foo. Ora, quando questa dichiarazione viene eseguito:

foo.x = foo = {a: 1}; 

la proprietà x è in realtà sempre aggiunto l'oggetto originale in memoria, mentre foo è sempre assegnato un puntatore ad un nuovo oggetto, {a: 1}. Ad esempio,

var foo, bar = foo = {}; 
foo.x = foo = {a: 1}; 

mostra che se foo e bar stanno indicando lo stesso oggetto inizialmente, bar (che sarà comunque puntare quell'oggetto originale) sarà simile {x: {a: 1}}, mentre foo è semplicemente {a: 1}.

Allora, perché non fa foo sembrano {a: 1, x: foo}?

Mentre tu hai ragione, i compiti sono giusti associativi, devi anche capire che l'interprete legge ancora da sinistra a destra. Facciamo un esempio approfondito (con alcuni bit sottratto out):

var foo = {}; 

Okay, create an object in memory location 47328 (or whatever), assign foo to a pointer that points to 47328.

foo.x = .... 

Okay, grab the object that foo currently points to at memory location 47328, add a property x to it, and get ready to assign x to the memory location of whatever's coming next.

foo = .... 

Okay, grab the pointer foo and get ready to assign it to the memory location of whatever's coming next.

{a: 1}; 

Okay, create a new object in memory at location 47452. Now go back up the chain: Assign foo to point to memory location 47452. Assign property x of the object at memory location 47328 to also point to what foo now points to--memory location 47452.

In breve, non c'è scorciatoia modo di fare

var foo = {a: 1}; 
foo.x = foo;