Valore primitivo vs Valore di riferimento

Ho letto un libro intitolato “Professional Javascript for web developer” e dice: “La variabile è assegnata dal valore di riferimento o dal valore primitivo. I valori di riferimento sono oggetti memorizzati nella memoria”. E poi non dice nulla su come viene memorizzato il valore primitivo. Quindi immagino che non sia archiviato in memoria. Sulla base di ciò, quando ho uno script come questo:

var foo = 123; 

In che modo Javascript ricorda la variabile foo per un uso futuro?

Una variable può contenere uno dei due tipi di primitive values : primitive values o reference values .

  • Primitive values sono dati che vengono memorizzati nello stack .
  • Primitive value viene memorizzato direttamente nella posizione a cui la variabile accede.
  • Reference values sono oggetti che sono memorizzati nell’heap .
  • Reference value memorizzato nella posizione variabile è un puntatore a una posizione nella memoria in cui è archiviato l’object.
  • I tipi primitivi includono Undefined , Null , Boolean , Number o String .

Le basi:

Gli oggetti sono aggregazioni di proprietà. Una proprietà può fare riferimento a un object o a una primitive . Primitives are values , non hanno proprietà.

aggiornato:

JavaScript ha 6 tipi di dati primitivi: stringa , numero , booleano , null , indefinito , simbolo (nuovo in ES6). Con l’eccezione di null e undefined, tutti i valori delle primitive hanno equivalenti object che avvolgono i valori primitivi, ad esempio un object String avvolge una stringa primitiva. Tutti i primitivi sono immutabili.

Ok, immagina che la tua variabile sia un pezzo di carta – una nota adesiva.

Nota 1: una variabile è una nota adesiva .

Ora, una nota adesiva è molto piccola. Puoi solo scrivere un po ‘di informazioni su di esso. Se vuoi scrivere più informazioni hai bisogno di più note adesive, ma non è un problema. Immagina di avere una scorta infinita di note adesive.

Nota 2: hai una scorta infinita di note adesive, che memorizzano piccole quantità di informazioni.

Ottimo, cosa puoi scrivere sulla tua nota adesiva? Posso scrivere:

  1. Sì o no (un booleano ).
  2. La mia età (un numero ).
  3. Il mio nome (una stringa ).
  4. Niente affatto ( indefinito ).
  5. Uno scarabocchio o qualsiasi altra cosa che non significa niente per me ( nulla ).

Quindi possiamo scrivere cose semplici (siamo condiscendenti e chiamiamole cose primitive ) sulle nostre note appiccicose.

Nota 3: puoi scrivere cose primitive sulle tue note adesive.

Quindi dico di scrivere 30 su una nota adesiva per ricordarmi di comprare 30 fette di formaggio per la piccola festa che sto lanciando al mio posto stasera (ho pochissimi amici).

Quando vado a mettere la mia nota sul frigorifero vedo che mia moglie ha messo un’altra nota sul frigorifero che dice anche 30 (per ricordarmi che il suo compleanno è il 30 di questo mese).

Q: Entrambe le note adesive trasmettono le stesse informazioni?

A: Sì, entrambi dicono 30 . Non sappiamo se sono 30 fette di formaggio o il 30 ° giorno del mese e francamente non ci interessa. Per una persona che non sapeva niente di meglio è lo stesso.

 var slicesOfCheese = 30; var wifesBirthdate = 30; alert(slicesOfCheese === wifesBirthdate); // true 

Nota 4: due note adesive su cui è scritta la stessa cosa trasmettono le stesse informazioni, anche se sono due note adesive diverse.

Sono davvero eccitato per stasera – uscire con vecchi amici, divertendomi. Poi alcuni dei miei amici mi chiamano e dicono che non saranno in grado di arrivare alla festa.

Quindi vado al mio frigo e cancelli il 30 sulla mia nota adesiva (non la nota appiccicosa di mia moglie – che la farebbe arrabbiare molto) e ne faccio un 20 .

Nota 5: puoi cancellare ciò che è scritto su una nota adesiva e scrivere qualcos’altro.

D: Va tutto bene, ma se mia moglie volesse scrivere un elenco di generi alimentari da prendere mentre andavo a prendere del formaggio. Avrebbe bisogno di scrivere una nota adesiva per ogni articolo?

A: No, avrebbe preso una lista più lunga di carta e avrebbe scritto l’elenco dei generi alimentari su quel foglio. Poi avrebbe scritto una nota adesiva che mi diceva dove trovare la lista dei generi alimentari.

Quindi cosa sta succedendo qui?

  1. Un elenco di generi alimentari non è ovviamente semplice (erm … primitivo ).
  2. Mia moglie ha scritto su un pezzo di carta più lungo.
  3. Ha scritto dove trovarlo in una nota appiccicosa.

Tesoro, l’elenco dei generi alimentari è sotto la tua tastiera.

Per ricapitolare:

  1. L’object reale (l’elenco di generi alimentari) è sotto la mia tastiera.
  2. La nota adesiva mi dice dove trovarlo (l’indirizzo dell’object).

Nota 6: i valori di riferimento sono riferimenti a oggetti (indirizzi in cui verranno trovati).

Q: Come facciamo a sapere quando due note adesive dicono la stessa cosa? Di ‘a mia moglie che ha fatto un’altra lista della spesa in caso avessi perso il primo, e ne ho scritto un’altra nota appiccicosa. Entrambe le liste dicono la stessa cosa, ma le note adesive dicono la stessa cosa?

A: No. La prima nota adesiva ci dice dove trovare la prima lista. Il secondo ci dice dove trovare la seconda lista. Non importa se le due liste dicono la stessa cosa. Sono due liste diverse.

 var groceryList1 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"]; var groceryList2 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"]; alert(groceryList1 === groceryList2); // false 

Nota 7: due note adesive trasmettono le stesse informazioni solo se si riferiscono allo stesso object.

Questo significa che se mia moglie ha fatto due bigliettini per ricordarmi dove si trova la lista della spesa, le due note adesive contengono le stesse informazioni. Così questo:

Tesoro, l’elenco dei generi alimentari è sotto la tua tastiera.

Contiene le stesse informazioni di:

Non dimenticare che l’elenco dei prodotti alimentari è sotto la tua tastiera.

In termini di programmazione:

 var groceryList1 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"]; var groceryList2 = groceryList1; alert(groceryList1 === groceryList2); // true 

Questo è tutto ciò che devi sapere sulle primitive e sui riferimenti in JavaScript. Non c’è bisogno di entrare in cose come heap e allocazione dynamic della memoria. Questo è importante se stai programmando in C / C ++.

Modifica 1: Oh, e la cosa importante è che quando si passano le variabili intorno si passa essenzialmente il passaggio di valori primitivi per valore e valori di riferimento per riferimento .

Questo è solo un modo elaborato per dire che copi tutto ciò che è scritto su una nota adesiva su un’altra (non importa se copi un valore primitivo o un riferimento ).

Quando si copiano i riferimenti, l’object a cui si fa riferimento non si muove (ad es. La lista della spesa di mia moglie rimarrà sempre sotto la mia tastiera, ma posso prendere la nota adesiva che ho copiato ovunque io voglia – la nota adesiva originale sarà ancora sul frigorifero).

Modifica 2: in risposta al commento pubblicato da @LacViet:

Bene per i principianti, stiamo parlando di JavaScript e JavaScript non ha uno stack o un heap . È un linguaggio dinamico e tutte le variabili in JavaScript sono dinamiche. Per spiegare la differenza, la confronterò con C.

Considera il seguente programma C:

 #include  int main() { int a = 10; int b = 20; int c = a + b; printf("%d", c); return 0; } 

Quando compiliamo questo programma otteniamo un file eseguibile. Il file eseguibile è diviso in più segmenti (o sezioni). Questi segmenti includono il segmento di stack, il segmento di codice, il segmento di dati, il segmento extra, ecc.

  1. Il segmento stack viene utilizzato per memorizzare lo stato del programma quando viene richiamata una funzione o un gestore di interrupt. Ad esempio quando la funzione f chiama la funzione g allora lo stato della funzione f (tutti i valori nei registri in quel momento) vengono salvati in una pila. Quando g restituisce il controllo a f questi valori vengono ripristinati.
  2. Il segmento di codice contiene il codice effettivo che deve essere eseguito dal processore. Contiene una serie di istruzioni che il processore deve eseguire come add eax, ebx (dove add è l’opcode e eax & ebx sono argomenti). Questa istruzione aggiunge il contenuto dei registri eax ed ebx e memorizza il risultato nel registro eax .
  3. Il segmento dati viene utilizzato per riservare spazio per le variabili. Per esempio nel programma sopra abbiamo bisogno di riservare spazio per i numeri interi a , b e c . Inoltre, dobbiamo anche riservare spazio per la costante di stringa "%d" . Le variabili riservate hanno quindi un indirizzo fisso in memoria (dopo il collegamento e il caricamento).
  4. Oltre a tutto ciò, il Sistema Operativo offre anche un po ‘di spazio in più. Questo è chiamato l’heap. Qualsiasi spazio aggiuntivo di cui hai bisogno viene assegnato da questo spazio. La memoria allocata in questo modo è chiamata memoria dynamic.

Vediamo un programma con memoria dynamic:

 #include  #include  int main() { int * a = malloc(3 * sizeof(int)); a[0] = 3; a[1] = 5; a[2] = 7; printf("a: %d\nb: %d\nc: %d\n", a[0], a[1], a[2]); return 0; } 

Perché vogliamo allocare la memoria in modo dinamico, dobbiamo usare i puntatori. Questo perché vogliamo utilizzare la stessa variabile per puntare a una posizione di memoria arbitraria (non necessariamente la stessa posizione di memoria ogni volta).

Quindi creiamo un puntatore int * ( int * ) chiamato a . Lo spazio per a viene assegnato dal segmento di dati (cioè non è dinamico). Quindi chiamiamo malloc per allocare lo spazio contiguo per 3 numeri interi dall’heap. L’indirizzo di memoria del primo int viene restituito e memorizzato nel puntatore a .

Q: cosa abbiamo imparato?

A: Viene assegnata una quantità fissa di spazio per tutte le variabili. Ogni variabile ha un indirizzo fisso. Possiamo anche allocare memoria extra dall’heap e memorizzare l’indirizzo di questa memoria extra in un puntatore. Questo è chiamato uno schema di memoria dynamic.

Concettualmente questo è simile a quello che ho spiegato a proposito delle variabili come note adesive. Tutte le variabili (compresi i puntatori sono note adesive). Tuttavia i puntatori sono speciali perché fanno riferimento a una posizione di memoria (che è come fare riferimento a un object in JavaScript).

Tuttavia questo è dove finiscono le somiglianze. Ecco le differenze:

  1. In C tutto viene passato per valore (inclusi gli indirizzi nei puntatori). Per passare un riferimento è necessario utilizzare l’indirezione tramite puntatori. JavaScript supera solo le primitive in base al valore. Il passaggio dei riferimenti viene gestito in modo trasparente dal motore ed è come passare qualsiasi altra variabile.
  2. In C puoi creare un puntatore a un tipo di dati primitivi come int . In JavaScript non è ansible creare un riferimento a un valore primitivo come il number . Tutti i primitivi vengono sempre memorizzati in base al valore.
  3. In C puoi eseguire varie operazioni sui puntatori. Questo è chiamato aritmetica del puntatore. JavaScript non ha puntatori. Ha solo riferimenti. Pertanto non è ansible eseguire alcun calcolo aritmetico.

Oltre a questi tre, la più grande differenza tra C e JavaScript è che tutte le variabili in JavaScript sono effettivamente dei puntatori. Poiché JavaScript è un linguaggio dinamico, è ansible utilizzare la stessa variabile per memorizzare un number e una string in diversi momentjs.

JavaScript è un linguaggio interpretato e l’interprete di solito è scritto in C ++. Pertanto tutte le variabili in JavaScript sono mappate agli oggetti nella lingua host (anche primitive).

Quando dichiariamo una variabile in JavaScript, l’interprete crea una nuova variabile generica per esso. Quindi quando assegniamo un valore (sia esso un primitivo o un riferimento), l’interprete semplicemente assegna ad esso un nuovo object. Internamente conosce quali oggetti sono primitivi e quali sono in realtà oggetti.

Concettualmente è come fare qualcosa del genere:

 JSGenericObject ten = new JSNumber(10); // var ten = 10; 

D: Cosa significa?

A: Significa che tutti i valori (primitive e oggetti) in JavaScript sono allocati dall’heap. Anche le variabili stesse sono allocate dall’heap. È sbagliato affermare che le primitive sono allocate dallo stack e che solo gli oggetti sono allocati dall’heap. Questa è la più grande differenza tra C e JavaScript.

In javascript i Primitive values sono dati che vengono memorizzati nello stack .

Primitive value viene memorizzato direttamente nella posizione a cui la variabile accede.

E i Reference values sono oggetti che sono memorizzati heap .

Il valore di riferimento memorizzato nella posizione variabile è un puntatore a una posizione nella memoria in cui è archiviato l’object.

JavaScript supporta cinque tipi di dati primitivi: number, string, Boolean, undefined, and null .

Questi tipi sono indicati come tipi primitivi perché sono i blocchi di base da cui è ansible build tipi più complessi.

Dei cinque, solo il number, string, and Boolean sono tipi di dati reali nel senso di memorizzare effettivamente i dati.

Undefined and null sono tipi che si presentano in circostanze speciali. Il primitive type ha una dimensione fissa in memoria. Ad esempio, un numero occupa otto byte di memoria e un valore booleano può essere rappresentato con un solo bit.

E i tipi di riferimento possono essere di qualsiasi lunghezza – non hanno una dimensione fissa.

Un tipo primitivo ha una dimensione fissa in memoria. Ad esempio, un numero occupa otto byte di memoria e un valore booleano può essere rappresentato con un solo bit. Il tipo di numero è il più grande dei tipi primitivi. Se ciascuna variabile JavaScript riserva otto byte di memoria, la variabile può contenere direttamente qualsiasi valore primitivo.

Questa è una semplificazione eccessiva e non è intesa come una descrizione di un’implementazione JavaScript reale.

I tipi di riferimento sono un’altra questione, comunque. Gli oggetti, per esempio, possono essere di qualsiasi lunghezza – non hanno una dimensione fissa. Lo stesso vale per gli array: un array può avere un numero qualsiasi di elementi. Allo stesso modo, una funzione può contenere qualsiasi quantità di codice JavaScript. Poiché questi tipi non hanno una dimensione fissa, i loro valori non possono essere memorizzati direttamente negli otto byte di memoria associati a ciascuna variabile. Invece, la variabile memorizza un riferimento al valore. In genere, questo riferimento è una forma di puntatore o indirizzo di memoria. Non è il valore dei dati stesso, ma indica alla variabile dove cercare il valore.

La distinzione tra tipi primitivi e di riferimento è importante, poiché si comportano diversamente. Si consideri il seguente codice che utilizza i numeri (un tipo primitivo):

 var a = 3.14; // Declare and initialize a variable var b = a; // Copy the variable's value to a new variable a = 4; // Modify the value of the original variable alert(b) // Displays 3.14; the copy has not changed 

Non c’è nulla di sorprendente in questo codice. Consideriamo ora cosa succede se cambiamo leggermente il codice in modo che utilizzi le matrici (un tipo di riferimento) anziché i numeri:

 var a = [1,2,3]; // Initialize a variable to refer to an array var b = a; // Copy that reference into a new variable a[0] = 99; // Modify the array using the original reference alert(b); // Display the changed array [99,2,3] using the new reference 

Se questo risultato non ti sembra sorprendente, hai già familiarità con la distinzione tra tipi primitivi e di riferimento. Se sembra sorprendente, dai un’occhiata più da vicino alla seconda riga. Si noti che è il riferimento al valore dell’array, non alla matrice stessa, che viene assegnata in questa istruzione. Dopo quella seconda riga di codice, abbiamo ancora un solo object array; abbiamo solo due riferimenti ad esso.

Un valore primitivo è un dato rappresentato al livello più basso dell’implementazione del linguaggio e in JavaScript è uno dei seguenti tipi: numero, stringa, booleano, non definito e null.