Come funziona l'assegnazione delle variabili in JavaScript?


97

Quindi stavo giocando l'altro giorno solo per vedere esattamente come funziona l'assegnazione di massa in JavaScript.

Per prima cosa ho provato questo esempio nella console:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

Il risultato è stato la "barra" visualizzata in un avviso. Questo è abbastanza giusto, ae in brealtà sono solo alias per lo stesso oggetto. Poi ho pensato, come avrei potuto rendere questo esempio più semplice.

a = b = 'foo';
a = 'bar';
console.log(b);

È più o meno la stessa cosa, non è vero? Ebbene, questa volta, ritorna foonon barcome mi sarei aspettato dal comportamento del primo esempio.

Perché succede questo?

NB Questo esempio potrebbe essere ulteriormente semplificato con il seguente codice:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(Sospetto che JavaScript tratti le primitive come stringhe e interi in modo diverso dagli hash. Gli hash restituiscono un puntatore mentre le primitive "core" restituiscono una copia di se stesse)


Spiegazione dell'assegnazione qui: syntaxsuccess.com/viewarticle/…
TGH

Risposte:


114

Nel primo esempio, stai impostando una proprietà di un oggetto esistente. Nel secondo esempio, stai assegnando un oggetto nuovo di zecca.

a = b = {};

ae bora sono puntatori allo stesso oggetto. Quindi quando lo fai:

a.foo = 'bar';

Imposta b.fooanche da allora ae bpunta allo stesso oggetto.

Però!

Se invece fai questo:

a = 'bar';

stai dicendo che ora apunta a un oggetto diverso. Questo non ha effetto su quanto aindicato prima.

In JavaScript, l'assegnazione di una variabile e l'assegnazione di una proprietà sono 2 operazioni diverse. È meglio pensare alle variabili come puntatori a oggetti e quando si assegna direttamente a una variabile, non si modifica alcun oggetto, si limita a rinominare la variabile in un oggetto diverso.

Ma assegnare una proprietà, come a.foo, modificherà l'oggetto a cui apunta. Questo, ovviamente, modifica anche tutti gli altri riferimenti che puntano a questo oggetto semplicemente perché puntano tutti allo stesso oggetto.


3
"stai dicendo che a punta a un oggetto diverso ora." No, non usare la parola oggetto. Una stringa non è un oggetto in JavaScript.
Nosredna

9
Forse le stringhe non sono tecnicamente del tipo javascript "Object", ma possono essere pensate come oggetti nel senso OO.
Alex Wayne

2
@Squeegy: le stringhe sono primitive, non oggetti: non puoi assegnare proprietà arbitrarie alle stringhe! Si comportano solo come oggetti a causa di ciò che viene chiamato autoboxing in Java
Christoph

11
Ma le stringhe hanno metodi e proprietà e il prototipo di String può essere sicuramente modificato. Di certo agiscono come oggetti.
Cameron Martin

9
@ddlshack: come ha spiegato Christoph, ciò è dovuto all'autoboxing. Se si dovesse definire una stringa via var foo = new String('foo');, quindi che sarebbe un oggetto stringa (e typeoflo confermerà). Ma se lo dichiari tramite una stringa letterale, allora sono primitive stringa. Vedi: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Lèse majesté

26

La tua domanda ha già ricevuto una risposta soddisfacente da Squeegy: non ha nulla a che fare con oggetti rispetto a primitive, ma con riassegnazione di variabili rispetto all'impostazione di proprietà nello stesso oggetto referenziato.

Sembra esserci molta confusione sui tipi di JavaScript nelle risposte e nei commenti, quindi ecco una piccola introduzione al sistema di tipi di JavaScript:

In JavaScript, ci sono due tipi di valori fondamentalmente diversi: primitivi e oggetti (e non c'è niente come un 'hash').

Stringhe, numeri e booleani così come nulle undefinedsono primitive, gli oggetti sono tutto ciò che può avere proprietà. Anche gli array e le funzioni sono oggetti regolari e quindi possono contenere proprietà arbitrarie. Differiscono solo nella proprietà interna [[Class]] (le funzioni hanno anche una proprietà chiamata [[Call]] e [[Construct]], ma ehi, questi sono i dettagli).

Il motivo per cui i valori primitivi possono comportarsi come oggetti è a causa dell'autoboxing, ma le primitive stesse non possono contenere alcuna proprietà.

Ecco un esempio:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

Questo produrrà undefined: acontiene un valore primitivo, che viene promosso a un oggetto quando si assegna la proprietà foo. Ma questo nuovo oggetto viene immediatamente scartato, quindi il valore di fooviene perso.

Pensa in questo modo:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created

La pagina della Mozilla Foundation "Una reintroduzione a JavaScript (tutorial JS)" descrive gli oggetti JavaScript "come semplici raccolte di coppie nome-valore. Come tali, sono simili a ...", quindi segue con un elenco di dizionari, hash , tabelle hash e mappe hash da vari linguaggi di programmazione. La stessa pagina descrive i riferimenti alle proprietà degli oggetti come ricerche nelle tabelle hash. Quindi gli oggetti sono tutto come una tabella "hash". Ciò non annulla le altre informazioni utili, ma la caratterizzazione originale di Chris Lloyd non era imprecisa.
C Perkins

2

Hai più o meno ragione, tranne per il fatto che ciò a cui ti riferisci come "hash" è in realtà solo una sintassi abbreviata per un oggetto.

Nel primo esempio, un e b si riferiscono entrambi allo stesso oggetto. Nel secondo esempio, modifichi a per fare riferimento a qualcos'altro.


Allora perché il doppio standard per Object?
Chris Lloyd

Non è un doppio standard. Nel primo esempio, aeb si riferiscono ancora allo stesso oggetto, stai solo modificando una proprietà di quell'oggetto. Nel secondo esempio, stai indicando un oggetto diverso.
Kevin

1
No, la differenza è che nel secondo caso hai a che fare con una stringa, non con un oggetto.
Nosredna

1
Per essere chiari: questo non ha nulla a che fare con le stringhe che restituiscono una copia di se stesse. Il motivo per cui i due frammenti di codice sono diversi è nel secondo paragrafo di Kevin (spiegato più completamente nella risposta di Squeegy).
Chuck

Non importa se hai una stringa o un oggetto nella variabile. Si assegna un nuovo valore diverso e quindi la variabile contiene quel nuovo valore diverso.
sth

2

ecco la mia versione della risposta:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory

0

Stai impostando a in modo che punti a un nuovo oggetto stringa, mentre b continua a puntare al vecchio oggetto stringa.


0

Nel primo caso si modificano alcune proprietà dell'oggetto contenuto nella variabile, nel secondo caso si assegna un nuovo valore alla variabile. Sono cose fondamentalmente diverse. Le variabili ae bnon sono in qualche modo magicamente collegate dal primo assegnamento, contengono solo lo stesso oggetto. Questo è anche il caso del secondo esempio, finché non si assegna un nuovo valore alla bvariabile.


0

La differenza è tra tipi e oggetti semplici.

Tutto ciò che è un oggetto (come un array o una funzione) viene passato per riferimento.

Tutto ciò che è un tipo semplice (come una stringa o un numero) viene copiato.

Ho sempre a portata di mano una funzione copyArray, quindi posso essere sicuro di non creare un gruppo di alias per lo stesso array.


La differenza non è evidente in molti scenari, ma Javascript in realtà non passa o assegna per riferimento. Copia i valori di riferimento.
Juan Pablo Califano

Questi ragazzi hanno già fatto un buon lavoro spiegandolo, quindi incollerò semplicemente il link: stackoverflow.com/questions/40480/is-java-pass-by-reference (mi riferisco a Java, ma la semantica per il passaggio e l'assegnazione i valori / i riferimenti sono gli stessi di Javascript)
Juan Pablo Califano

1
In realtà questa risposta non è corretta, tutto viene passato per valore in JavaScript. Da MDN, "I parametri di una chiamata di funzione sono gli argomenti della funzione. Gli argomenti vengono passati alle funzioni in base al valore. Se la funzione modifica il valore di un argomento, questa modifica non viene riflessa globalmente o nella funzione chiamante. Tuttavia, i riferimenti agli oggetti sono anche i valori e sono speciali: se la funzione cambia le proprietà dell'oggetto di riferimento, quel cambiamento è visibile al di fuori della funzione. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bittersweetryan

Le primitive si comportano come oggetti immutabili ( esattamente come loro in modalità rigorosa). Questa risposta non è corretta.
Ry-
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.