Почему я могу изменить значение константы в javascript

102

Я знаю, что ES6 еще не стандартизирован, но многие браузеры в настоящее время поддерживают const ключевое слово в JS.

В спецификации написано, что:

Значение константы не может измениться посредством повторного присвоения, и константа не может быть повторно объявлена. Из-за этого, хотя можно объявить константу без ее инициализации, это было бы бесполезно.

и когда я делаю что-то вроде этого:

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

Вижу, что все ок, xxxпо-прежнему 6и yyyесть [].

Но если я это сделаю yyy.push(6); yyy.push(1);, мой постоянный массив изменится. Прямо сейчас он есть, [6, 1]и я, кстати, до сих пор не могу его изменить yyy = 1;.

Это ошибка или я что-то упускаю? Пробовал в последнем хроме и FF29

Сальвадор Дали
источник
1
Можете ли вы просто создать класс, объявить переменную и присвоить ей значение внутри класса. Затем создайте GETTER для этой переменной; и не реализуйте сеттер. Он должен реализовать постоянную ...
Андрей
8
@ Андрей, спасибо, но я не спрашиваю, как я могу это сделать. Мне любопытно, почему ключевое слово const ведет себя так.
Сальвадор Дали

Ответы:

174

В документации указано:

... константа не может быть изменена путем переназначения
... константа не может быть повторно объявлена

Когда вы добавляете в массив или объект, вы не переназначаете или повторно объявляете константу, она уже объявлена ​​и назначена, вы просто добавляете ее в «список», на который указывает константа.

Итак, это отлично работает:

const x = {};

x.foo = 'bar';

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

x.foo = 'bar2';

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

и это:

const y = [];

y.push('foo');

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

y.unshift("foo2");

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

y.pop();

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

но ни то, ни другое:

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
значит, вы имеете в виду, что это не ошибка, но так должно работать? Я думал, что константа не может быть изменена. Обычно программист верит, что что бы ни случилось, ничто не может изменить значение внутри моей константы.
Сальвадор Дали
2
Думаю, это не так просто, в данном случае значение константы - это массив определенных элементов. Изменение чего-либо означает изменение значения .
Veritas
6
Да, это должно работать таким образом, вы не переназначаете константу, это все та же ссылка, вы просто добавляете в массив ссылки на константы, а массивы и объекты похожи на «списки», их изменение делает не изменять ссылку или повторно объявлять константу.
adeneo
27
@SalvadorDali: постоянный и доступный только для чтения - две разные вещи. Ваша переменная постоянна , но массив, на который она указывает, не предназначен только для чтения
Мэтт Берланд
46

Это происходит потому, что ваша константа фактически хранит ссылку на массив. Когда вы присоединяете что-то к своему массиву, вы изменяете не постоянное значение, а массив, на который оно указывает. То же самое произойдет, если вы назначите объект константе и попытаетесь изменить любое ее свойство.

Если вы хотите заморозить массив или объект, чтобы его нельзя было изменить, вы можете использовать Object.freezeметод, который уже является частью ECMAScript 5.

const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]
Гильерме Сен
источник
1
По той же логике константа, fiveустановленная на 5, на самом деле не имеет значения 5, это просто ссылка на число 5. Поэтому, если я это five++сделаю, я не изменяю константу, а только число, на которое она указывает.
Энтони
3
@Anthony, эталонная штука работает только для массивов и объектов, а не для примитивных значений
Гильерме Сен
1
@Anthony В вашем примере вы меняете число, на которое fiveуказывает переменная (переменная fiveраньше была меткой для числа 5, теперь она указывает на другое число: 6). В примере в вопросе (и в этом ответе) xвсегда указывает на один и тот же список; если xэто const, вы не можете указать на другой список. Единственная разница в том, что один и тот же список может увеличиваться или уменьшаться; это возможно только для массивов и объектов, но не для примитивов.
ShreevatsaR
9

Это последовательное поведение со всеми языками программирования, которые я могу придумать.

Рассмотрим C - массивы - это просто прославленные указатели. Постоянный массив означает только то, что значение указателя не изменится, но на самом деле данные, содержащиеся по этому адресу, свободны.

В javascript вам разрешено вызывать методы константных объектов (конечно - иначе константные объекты не имели бы особого смысла!). Эти методы могут иметь побочный эффект изменения объекта. Поскольку массивы в javascript являются объектами, это поведение применимо и к ним.

Все, в чем вы уверены, - это то, что константа всегда будет указывать на один и тот же объект. Свойства самого объекта можно изменять.

четверг
источник
4

Объявление const создает доступную только для чтения ссылку на значение. Это не означает, что хранимое в нем значение неизменяемо, просто идентификатор переменной нельзя переназначить. Например, в случае, когда содержимое является объектом, это означает, что содержимое объекта (например, его параметры) может быть изменено.

Кроме того, еще одно важное замечание:

Глобальные константы не становятся свойствами оконного объекта ...

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

Бенджамин Уэст
источник
3

Я думаю, это дало бы вам больше ясности в вопросе: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 .

По сути, это сводится к тому, что constвсегда указывается один и тот же адрес в памяти. Вы можете изменить значение, хранящееся в этом адресе, но не можете изменить адрес, на constкоторый он указывает.

Упомянутое constвами определение будет истинным, когда constуказывает на адрес, содержащий примитивное значение. Это связано с тем, что вы не можете присвоить ему значение, constне изменив его адрес (потому что так работает присвоение примитивных значений), а изменение адреса a constзапрещено.

Если, как если бы constэто указывает на непримитивное значение, можно изменить значение адреса.

Zyxmn
источник
2

Я прочитал эту статью, пока искал, почему мне удалось обновить объект даже после определения его как const. Итак, дело здесь в том, что обновлять можно не непосредственно объект, а содержащиеся в нем атрибуты.

Например, мой объект выглядит так:

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

В приведенных выше ответах правильно указано, что это объект, который является константой, а не его атрибутом. Следовательно, я смогу обновить идентификатор или имя, выполнив:

number.name = 'John';

Но я не смогу обновить сам объект, например:

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

TypeError: Assignment to constant variable.
Атул О Холич
источник
1
Ваш пример практический и правильные описания
Ибрагим
0

Поскольку в const вы можете изменять значения объекта, поэтому объект фактически не хранит данные о назначении, а вместо этого указывает на них. так что есть разница между примитивами и объектами в Javascript.


источник