Por que posso alterar o valor de uma constante em javascript

101

Eu sei que o ES6 ainda não está padronizado, mas muitos navegadores atualmente oferecem suporte a const palavras-chave em JS.

Na especificação, está escrito que:

O valor de uma constante não pode ser alterado por meio da reatribuição, e uma constante não pode ser declarada novamente. Por causa disso, embora seja possível declarar uma constante sem inicializá-la, seria inútil fazê-lo.

e quando faço algo assim:

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

Vejo que está tudo bem, xxxainda está 6e yyyestá [].

Mas se eu fizer isso yyy.push(6); yyy.push(1);, meu array constante foi alterado. Agora é [6, 1]e, por falar nisso, ainda não posso mudar com yyy = 1;.

Eu sou um bug ou estou faltando alguma coisa? Eu tentei no mais recente Chrome e FF29

Salvador Dalí
fonte
1
Você pode simplesmente criar uma classe, declarar a variável e atribuir seu valor dentro da classe. Em seguida, crie um GETTER para essa variável; e não implemente um setter. Deve implementar uma constante ...
André
8
@Andrew obrigado, mas não estou perguntando como posso fazer isso. Estou curioso para saber por que a palavra-chave const se comporta dessa maneira.
Salvador Dali

Respostas:

171

A documentação afirma:

... constante não pode mudar por meio de reatribuição
... constante não pode ser declarada novamente

Quando você está adicionando a um array ou objeto, você não está reatribuindo ou declarando novamente a constante, ela já está declarada e atribuída, você está apenas adicionando à "lista" para a qual a constante aponta.

Então, isso funciona bem:

const x = {};

x.foo = 'bar';

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

x.foo = 'bar2';

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

e isto:

const y = [];

y.push('foo');

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

y.unshift("foo2");

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

y.pop();

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

mas nenhum destes:

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
adeneo
fonte
4
então você quer dizer que isso não é um bug, mas deveria funcionar assim? Porque eu pensei que a ideia da constante é que ela não pode ser alterada. Basicamente, um programador tem confiança de que não importa o que aconteça, nada pode alterar o valor dentro da minha constante.
Salvador Dali
2
Acho que não é tão fácil, neste caso o valor da constante é um array de elementos específicos. Alterar qualquer coisa significa que você alterou o valor .
veritas
6
Sim, é suposto funcionar desta forma, você não está reatribuindo a constante, ainda é a mesma referência, você está apenas adicionando ao array as referências constantes, e arrays e objetos são como "listas", modificá-los faz não altere a referência ou declare novamente a constante.
adeneo
26
@SalvadorDali: constante e somente leitura são duas coisas diferentes. Sua variável é constante , mas a matriz para a qual ela aponta não é somente leitura
Matt Burland
43

Isso acontece porque sua constante está, na verdade, armazenando uma referência ao array. Quando você une algo em seu array, você não está modificando seu valor constante, mas o array para o qual ele aponta. O mesmo aconteceria se você atribuísse um objeto a uma constante e tentasse modificar qualquer propriedade dela.

Se você deseja congelar uma matriz ou objeto para que não possa ser modificado, você pode usar o Object.freezemétodo, que já faz parte do ECMAScript 5.

const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]
Guilherme Sehn
fonte
1
Pela mesma lógica, uma constante fivedefinida como 5 não tem realmente um valor de 5, é apenas uma referência ao número 5. Portanto, se eu fizer five++isso, não mudarei a constante, apenas o número para o qual ela aponta.
Anthony
3
@Anthony a referência só funciona para arrays e objetos, não para valores primitivos
Guilherme Sehn 19/07/18
1
@Anthony Em seu exemplo, você está mudando o número para o qual a variável fiveaponta (a variável fivecostumava ser um rótulo para o número 5, agora está apontando para um número diferente: 6). No exemplo da pergunta (e esta resposta), xsempre aponta para a mesma lista; se xfor const, você não pode fazer com que aponte para uma lista diferente. A única diferença é que a mesma lista pode aumentar ou diminuir; isso é algo possível apenas para matrizes e objetos e não para primitivos.
ShreevatsaR
9

Este é um comportamento consistente com todas as linguagens de programação que posso imaginar.

Considere C - arrays são apenas ponteiros glorificados. Um array constante significa apenas que o valor do ponteiro não mudará - mas na verdade os dados contidos naquele endereço estão livres para.

Em javascript, você tem permissão para chamar métodos de objetos constantes (é claro - caso contrário, objetos constantes não serviriam a muitos propósitos!) Esses métodos podem ter o efeito colateral de modificar o objeto. Como os arrays em javascript são objetos, esse comportamento também se aplica a eles.

Você só tem certeza de que a constante sempre apontará para o mesmo objeto. As propriedades do próprio objeto podem ser alteradas.

thorsday
fonte
4

A declaração const cria uma referência somente leitura para um valor. Isso não significa que o valor que contém seja imutável, apenas que o identificador da variável não pode ser reatribuído. Por exemplo, no caso em que o conteúdo é um objeto, isso significa que o conteúdo do objeto (por exemplo, seus parâmetros) pode ser alterado.

Além disso, uma observação também importante:

Constantes globais não se tornam propriedades do objeto janela ...

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

Benjamin West
fonte
3

Acho que isso lhe daria mais clareza sobre o assunto: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 .

Basicamente, tudo se resume a constsempre apontar para o mesmo endereço na memória. Você pode alterar o valor armazenado naquele endereço, mas não pode alterar o endereço que constestá apontando também.

A definição de constvocê mencionada será verdadeira quando o constestiver apontando para um endereço que contém um valor primitivo. Isso ocorre porque você não pode atribuir um valor a this constsem alterar seu endereço (porque é assim que a atribuição de valores primitivos funciona) e alterar o endereço de a constnão é permitido.

Onde como se estivesse constapontando para um valor não primitivo, é possível editar o valor do endereço.

Zyxmn
fonte
1

Li este artigo enquanto pesquisava por que fui capaz de atualizar um objeto mesmo depois de defini-lo como const. Portanto, o ponto aqui é que não é o objeto diretamente, mas os atributos que ele contém que podem ser atualizados.

Por exemplo, meu objeto se parece com:

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

As respostas acima indicaram corretamente que é o objeto que é const e não seu atributo. Portanto, serei capaz de atualizar o id ou nome fazendo:

number.name = 'John';

Mas, não poderei atualizar o próprio objeto como:

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

TypeError: Assignment to constant variable.
Atul O Holic
fonte
1
seu exemplo é prático e descrições corretas
Ebrahim
0

Porque em const você pode alterar os valores de um objeto, então o objeto não armazena realmente os dados de atribuição, mas em vez disso, aponta para eles. portanto, há uma diferença entre primitivos e objetos em Javascript.


fonte