As stated in summary section of the documentation of const
:
To statement const
creates a variable whose value is fixed, that is, a read-only constant. This does not mean that the value is immutable, only that the constant variable cannot be changed or re-assigned.
This means that you cannot create another variable with the same name, since a read-only reference has been created. However, you can still change the value of the constant.
This is clearly visible in objects:
// Note `const`:
const person = { name: 'Fulano' };
console.log(person.name);
// O valor que a variável armazena pode ser alterado.
// Só não pode ser reatribuído.
//
// Então isso está OK:
person.name = 'Luiz Felipe';
console.log(person.name);
Or in arrays, as per your example.
If you want to create a really immutable object, you can use the Object.freeze
, that "freezes" past reference. For example:
const arr = [1, 2, 3];
Object.freeze(arr); // A partir de agora, `arr` está "congelada".
const obj = { name: 'Luiz Felipe' };
Object.freeze(obj); // A partir de agora, `obj` está "congelado".
But why does it really happen?
Every Javascript object has its properties (this is not new to us), the thing is that each of these properties has some so-called "metadata" Property Descriptors, that configure the behavior of that specific property. These Property Descriptors have some attributes, are they:
configurable
, that will be true (true
) if the property can be changed and deleted from its object.
writable
, which will be true if the property can be changed using some allocation operator.
enumerable
, which will be true if the property is shown during the enumeration of its object. For example, in Object.keys
or in bonds for..in
.
value
, representing the value associated with the property in question.
The documentation gives a good idea about this. There’s also the question "What and how Javascript property descriptors and attributes work?", which summarises their operation.
Thus, whenever you create a literal object, all its properties have their descriptors defined with enumerable
, configurable
and writable
defined as true
. This means that every literal created object can have its properties changed, deleted and the like.
So when you use the Object.freeze
, object properties will have their descriptors changed to define enumerable
and writable
as false
. The object will also suffer extension prevention, which will not allow new properties to be added to it. Essentially, the Object.freeze
makes any object truly immutable.
We can observe this behavior using the Object.getOwnPropertyDescriptors
. Behold:
const obj = { name: 'Luiz Felipe', username: 'lffg' };
// Note na saída que cada propriedade (`name` e `username` do objeto `obj`)
// possui os descritores `writable` e `configurable` definidos como `true`.
console.log(Object.getOwnPropertyDescriptors(obj));
console.log('====== Congelou ======');
Object.freeze(obj);
// Note agora que os descritores `writable` e `configurable` estão como `false`.
console.log(Object.getOwnPropertyDescriptors(obj));
And the same goes for arrays, since in Javascript, all array is an object, and roughly, the indices are the "properties". Therefore, the behavior is exactly the same:
const arr = [1, 2, 3];
// Note na saída que cada índice (`0`, `1` e `2` do array `arr`)
// possui os descritores `writable` e `configurable` definidos como `true`.
console.log(Object.getOwnPropertyDescriptors(arr));
console.log('====== Congelou ======');
Object.freeze(arr);
// Note agora que os descritores `writable` e `configurable` estão como `false`.
console.log(Object.getOwnPropertyDescriptors(arr));
Finally, I want to make it clear that it is not the statement const
defining writable
and configurable
as true
by default. It is the fact that you create the object using the literal form. You can create "fully configured" objects using the method Object.create
. The Object.defineProperty
allows you to create one property configured at a time. And finally, the Object.defineProperties
allows you to create multiple properties configured at once.
But don’t worry about all that. This is more related to metaprogramming. Are rarely used directly.
Because when you create a constant, you’re informing that you don’t want there to be a reallocation*, that is, it is not possible to do something like
array = [1, 2]
. If you want to prevent the structure from being modified, then you are looking for immutable data structures.– Jéf Bueno
To summarize, the object does not change when it uses
array.pop
, only changes the content within it, you could create a class and "instantiate it" right in theconst
and then take one of the property and set a new valueconst = new foo; foo.x = 1
. The instance remains the same. Vale remembers that the property values of an object function as a reference, but the value itself applied to a variable or constant does not.– Guilherme Nascimento