Perché posso cambiare il valore di una costante in javascript


101

So che ES6 non è ancora standardizzato, ma molti browser attualmente supportano le const parole chiave in JS.

Nelle specifiche è scritto che:

Il valore di una costante non può cambiare tramite riassegnazione e una costante non può essere dichiarata nuovamente. Per questo motivo, sebbene sia possibile dichiarare una costante senza inizializzarla, sarebbe inutile farlo.

e quando faccio qualcosa del genere:

const xxx = 6;
xxx = 999;
xxx++;
const yyy = [];
yyy = 'string';
yyy = [15, 'a'];

Vedo che tutto è ok xxxè ancora 6ed yyyè [].

Ma se lo faccio yyy.push(6); yyy.push(1);, il mio array costante è stato modificato. In questo momento lo è [6, 1]e comunque non riesco ancora a cambiarlo yyy = 1;.

Questo è un bug o mi manca qualcosa? L'ho provato con le ultime Chrome e FF29


1
Puoi semplicemente creare una classe, dichiarare la variabile e assegnarne il valore all'interno della classe. Quindi, crea un GETTER per quella variabile; e non implementare un setter. Dovrebbe implementare una costante ...
Andrew

8
@ Andrew, grazie, ma non chiedo come posso farlo. Sono curioso del motivo per cui la parola chiave const si comporta in questo modo.
Salvador Dali

Risposte:


172

La documentazione afferma:

... la costante non può cambiare attraverso la riassegnazione
... la costante non può essere ri-dichiarata

Quando aggiungi a un array o un oggetto non stai riassegnando o ri-dichiarando la costante, è già dichiarata e assegnata, stai semplicemente aggiungendo alla "lista" a cui punta la costante.

Quindi funziona bene:

const x = {};

x.foo = 'bar';

console.log(x); // {foo : 'bar'}

x.foo = 'bar2';

console.log(x); // {foo : 'bar2'}  

e questo:

const y = [];

y.push('foo');

console.log(y); // ['foo']

y.unshift("foo2");

console.log(y); // ['foo2', 'foo']

y.pop();

console.log(y); // ['foo2']

ma nessuno di questi:

const x = {};
x = {foo: 'bar'}; // error - re-assigning

const y = ['foo'];
const y = ['bar']; // error - re-declaring

const foo = 'bar'; 
foo = 'bar2';       // error - can not re-assign
var foo = 'bar3';   // error - already declared
function foo() {};  // error - already declared

4
quindi vuoi dire che questo non è un bug, ma dovrebbe funzionare in questo modo? Perché pensavo che l'idea della costante fosse che non può essere cambiata. Fondamentalmente un programmatore ha fiducia che, qualunque cosa accadrà, nulla può cambiare il valore all'interno della mia costante.
Salvador Dali

2
Penso che non sia così facile, in questo caso il valore della costante è un array di elementi specifici. Modificare qualsiasi cosa significa modificare il valore .
veritas

6
Sì, dovrebbe funzionare in questo modo, non stai riassegnando la costante, è sempre lo stesso riferimento, stai solo aggiungendo all'array i riferimenti costanti e gli array e gli oggetti sono come "liste", modificarli fa non modificare il riferimento o dichiarare nuovamente la costante.
adeneo

26
@SalvadorDali: costante e di sola lettura sono due cose diverse. La tua variabile è costante , ma l'array a cui punta non è di sola lettura
Matt Burland

43

Ciò accade perché la tua costante memorizza effettivamente un riferimento all'array. Quando unisci qualcosa nel tuo array non stai modificando il tuo valore costante, ma l'array a cui punta. Lo stesso accadrebbe se si assegnasse un oggetto a una costante e si provasse a modificarne una qualsiasi proprietà.

Se vuoi congelare un array o un oggetto in modo che non possa essere modificato, puoi utilizzare il Object.freezemetodo, che fa già parte di ECMAScript 5.

const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]

1
Con la stessa logica, una costante fiveimpostata su 5 non ha effettivamente un valore di 5, è solo un riferimento al numero 5. Quindi se lo faccio five++non sto cambiando la costante, ma solo il numero a cui punta.
Anthony

3
@Anthony la cosa di riferimento funziona solo per array e oggetti, non valori primitivi
Guilherme Sehn,

1
@Anthony Nel tuo esempio, stai cambiando il numero a cui fivepunta la variabile (la variabile fiveera un'etichetta per il numero 5, ora punta a un numero diverso: 6). Nell'esempio nella domanda (e in questa risposta), xpunta sempre allo stesso elenco; se xè const non puoi fargli puntare a un elenco diverso. L'unica differenza è che lo stesso elenco può aumentare o diminuire; questo è possibile solo per array e oggetti e non per primitive.
ShreevatsaR

9

Questo è un comportamento coerente con ogni linguaggio di programmazione a cui riesco a pensare.

Considera che gli array C sono solo puntatori glorificati. Un array costante significa solo che il valore del puntatore non cambierà, ma in realtà i dati contenuti a quell'indirizzo sono liberi di farlo.

In javascript, puoi chiamare metodi di oggetti costanti (ovviamente - altrimenti gli oggetti costanti non servirebbero a molto!) Questi metodi potrebbero avere l'effetto collaterale di modificare l'oggetto. Poiché gli array in javascript sono oggetti, questo comportamento si applica anche a loro.

Tutto ciò di cui hai la certezza è che la costante punterà sempre allo stesso oggetto. Le proprietà dell'oggetto stesso sono libere di cambiare.


4

La dichiarazione const crea un riferimento di sola lettura a un valore. Non significa che il valore che contiene sia immutabile, ma solo che l'identificatore della variabile non può essere riassegnato. Ad esempio, nel caso in cui il contenuto sia un oggetto, ciò significa che il contenuto dell'oggetto (ad esempio, i suoi parametri) può essere modificato.

Inoltre, un'altra nota importante:

Le costanti globali non diventano proprietà dell'oggetto finestra ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const


3

Penso che questo ti darebbe maggiore chiarezza sulla questione: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 .

Fondamentalmente si riduce al constpuntare sempre allo stesso indirizzo in memoria. È possibile modificare il valore memorizzato in quell'indirizzo ma non è possibile modificare anche l'indirizzo a cui constpunta.

La definizione di constcui hai parlato rimarrà vera quando constpunta a un indirizzo che contiene un valore primitivo. Questo perché non è possibile assegnare un valore a questo constsenza cambiarne l'indirizzo (perché è così che funziona l'assegnazione di valori primitivi) e constnon è consentito modificare l'indirizzo di a .

Dove come se constpuntasse a un valore non primitivo, è possibile modificare il valore dell'indirizzo.


1

Ho esaminato questo articolo mentre cercavo il motivo per cui sono stato in grado di aggiornare un oggetto anche dopo averlo definito come const. Quindi il punto qui è che non è direttamente l'oggetto ma gli attributi che contiene che possono essere aggiornati.

Ad esempio, il mio oggetto ha il seguente aspetto:

const number = {
    id:5,
    name:'Bob'
};

Le risposte precedenti hanno correttamente sottolineato che è l'oggetto che è const e non il suo attributo. Quindi, sarò in grado di aggiornare l'ID o il nome eseguendo:

number.name = 'John';

Ma non sarò in grado di aggiornare l'oggetto stesso come:

number = {
    id:5,
    name:'John'
  };

TypeError: Assignment to constant variable.

1
il tuo esempio è pratico e descrizioni corrette
Ebrahim

0

Perché in const puoi modificare i valori di un oggetto, quindi l'oggetto non memorizza effettivamente i dati dell'assegnazione ma invece punta ad esso. quindi c'è una differenza tra primitive e oggetti in Javascript.

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.