Puntatore Javascript / follia di riferimento. Qualcuno può spiegarlo?

Javascript passa gli oggetti per riferimento. Questo ha perfettamente senso. Ma una volta che inizi a manipolare quegli oggetti, tutto si comporta in un modo che sembra non intuitivo. Lasciatemi offrire un esempio:

var a, b; a = {} b = a; a['one'] = {}; console.log( JSON.stringify(a) ); // outputs: {"one":{}} console.log( JSON.stringify(b) ); // outputs: {"one":{}} 

Questo è tutto a posto, perché ora b ha un puntatore a, quindi è prevedibile che anche l’assegnazione di cose a a avrà effetto su b .

Ma allora se lo faccio:

 a = a['one']; console.log( JSON.stringify(a) ); // outputs: {} console.log( JSON.stringify(b) ); // outputs: {"one":{}} 

Questo è sorprendente per me. Mi aspetterei che a e b siano uguali (e di essere {} poiché a['one'] stato precedentemente impostato su {} e a stato impostato su a['one'] ).

Ma non è questo il caso. Sembra che a perde il suo riferimento a b quando è assegnato a qualcosa di nuovo, ma b mantiene il valore che a stato impostato prima di perdere il riferimento a b .

Ma allora se lo faccio:

 a['two'] = 2; console.log( JSON.stringify(a) ); // outputs: {"two":2} console.log( JSON.stringify(b) ); // outputs: {"one":{"two":2}} 

Che cosa? a ha chiaramente perso il riferimento a b , ma b sembra avere ancora qualche riferimento a a .

L’object vuoto {} punta a qualche punto in memoria, quindi ogni variabile che fa riferimento a questo sta puntando allo stesso punto?

Qualcuno con una solida conoscenza di questo può spiegarmelo?

Seguendo il tuo esempio riga per riga:

 a = {} 

a ora fa riferimento al nuovo object.

 b = a; 

b ora fa riferimento allo stesso object di a riferimento. Si noti che non fa riferimento a .

 a['one'] = {}; 

Il nuovo object ora ha un indice 'one' che fa riferimento a un altro nuovo object.

Quando lo fai

 a = a['one']; 

Stai impostando a fare riferimento a a['one'] , che è quel nuovo object che hai creato quando hai fatto a['one'] = {} . b ancora riferimento all’object che hai creato con a = {} .

Stai confondendo il problema quando dici ” a ha perso il riferimento a b ” perché a non si riferisce a b , né viceversa. a e b riferiscono agli oggetti e possono essere fatti riferirsi ad altri oggetti. Come questo:

Con a = {}; b = a a = {}; b = a , ottieni

 a \ \ { } / / b 

Quindi con a['one'] = {} ottieni

 a \ \ { one: { } } / / b 

Quindi con a = a['one'] ottieni

 a - - - - \ { one: { } } / / b 

: P Stai scendendo nei dettagli grintosi e sono felice che tu abbia chiesto, dato che sarai più saggio alla fine.

Non guardarlo in termini di puntatori, perché penso che sia dove ti stai confondendo. Pensaci piuttosto in termini di heap (o solo “memoria” se vuoi) e la tabella dei simboli.

Iniziamo prendendo le prime righe del tuo codice:

 var a, b; a = {} b = a; 

Quello che hai fatto qui è creato un object sull’heap e due simboli sulla tabella dei simboli. Sembra qualcosa del genere:


Tabella dei simboli :

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400000 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

Mucchio :

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 |  | +----------+-----------------+ 

.


Ecco dove le cose si fanno interessanti: gli oggetti hanno le loro “tabelle dei simboli” (di solito queste sono solo tabelle hash, ma chiamarle una tabella dei simboli può renderle più chiare).

Ora, dopo la tua prossima affermazione, hai 3 cose da considerare: la tabella dei simboli globale, la tabella dei simboli e l’heap.

Esegui la seguente riga:

 a['one'] = {} 

E ora le cose sembrano così:


Tabella dei simboli globali :

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400000 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

Tabella dei simboli di

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | one | 0x400004 | +--------+-----------------+ 

Mucchio :

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 |  | +----------+-----------------+ | 0x400004 |  | <---we created a new object on the heap +----------+-----------------+ 

.


Ora hai eseguito il seguente codice:

 a = a['one']; 

Questo dovrebbe sembrare un cambiamento insignificante. Il risultato è:


Tabella dei simboli globali :

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400004 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

Tabella dei simboli di

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | one | 0x400004 | +--------+-----------------+ 

Mucchio :

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 |  | +----------+-----------------+ | 0x400004 |  | +----------+-----------------+ 

.


Spero che seguendo le posizioni di memoria per l'heap sia chiaro perché hai ottenuto l'output che hai fatto.

Ora le cose diventano ancora più interessanti, perché ora stai facendo:

 a['two'] = 2; 

Ok, facciamo questo passo dopo passo.

  • a punto alla posizione di memoria 0x400004 che contiene
  • è un object vuoto, quindi la sua tabella dei simboli inizia vuota
  • Eseguendo questa linea, aggiungiamo la variabile 'due' alla tabella dei simboli di .

Se non sei stanco di guardare ancora questi schemi, lo sarai. Le cose ora sembrano così:


Tabella dei simboli globali :

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | a | 0x400004 | +--------+-----------------+ | b | 0x400000 | +--------+-----------------+ 

Tabella dei simboli di

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | one | 0x400004 | +--------+-----------------+ 

Tabella dei simboli di

 +--------+-----------------+ | Symbol | Memory Location | +--------+-----------------+ | two | 0x400008 | +--------+-----------------+ 

Mucchio :

 +----------+-----------------+ | Location | Value | +----------+-----------------+ | 0x400000 |  | +----------+-----------------+ | 0x400004 |  | +----------+-----------------+ | 0x400008 | 2 (literal val) | <-- yes, even integers are stored on the heap +----------+-----------------+ in JavaScript. 

.


Se segui diligentemente il tempo per seguire le posizioni di memoria, vedrai che il tuo browser ha visualizzato l'output corretto.

Pensa all’object anonimo come se avesse un nome:

 a = {}; // The variable "a" now points to (holds) an anonymous object. b = a; // "b" points to the same anonymous object held by "a". a = 123; // "a" now holds some other value. b; // "b" still holds the anonymous object. 

La chiave è ricordare che le variabili contengono riferimenti ad oggetti , non riferimenti ad altre variabili. E lo stesso object può essere riferito da un numero qualsiasi di variabili.

Gli oggetti in Javascript possono esistere da soli senza bisogno di un nome. Per esempio:

 {} 

è una nuova istanza di un object dizionario.

 a = {}; 

crea un nuovo object dizionario e fa riferimento ad esso. Adesso

 b = a; 

fa b riferirsi allo stesso object sottostante. Puoi quindi fare a punto da qualche altra parte:

 a = "hi"; 

e b punta ancora allo stesso object dizionario che ha fatto prima. Il comportamento di b non è correlato al modo in cui si modifica ciò a punta.

Per quanto ne so, hai sovrascritto un errore, quindi credo che il motore lo salvi in ​​un altro spazio di memoria, mentre b continua a puntare al vecchio indirizzo di memoria (che in qualche modo non viene distrutto).