ES6 const: иммутабельность ни при чём
Похоже, это весьма распространённое заблуждение, которое не исчезнет просто так. Я натыкаюсь на него в блогах, twitter-обсуждениях и даже книгах. Эта статья — моя попытка расставить все точки над i.
const
создаёт неизменяемую связь
Ключевое слово const
не означает, что значение является «константой» или
что оно иммутабельно (неизменяемо). const
-значение определённо может
изменяться. Следующий пример выполнится без ошибок — это абсолютно валидный
ES6-код:
const foo = {};
foo.bar = 42;
console.log(foo.bar);
// → 42
Единственная действительно иммутабельная вещь в этом примере — связь между
именем переменной и её значением. const
присваивает значение {}
переменной
foo
и гарантирует, что этой переменной больше не будет присвоено никаких
других значений. Последующее использование операторов присваивания, а также
унарных или постфиксных --
и ++
вместе с этой
переменной вызовет исключение TypeError
:
const foo = 27;
// Любая из следующих незакомментированных строк вызовет исключение.
// Операторы присваивания:
foo = 42;
foo *= 42;
foo /= 42;
foo %= 42;
foo += 42;
foo -= 42;
foo <<= 0b101010;
foo >>= 0b101010;
foo >>>= 0b101010;
foo &= 0b101010;
foo ^= 0b101010;
foo |= 0b101010;
// Унарные операторы `--` и `++`:
--foo;
++foo;
// Постфиксы `--` и `++`:
foo--;
foo++;
ES6 const
не имеет ничего общего с иммутабельностью значений переменных.
И как тогда сделать значение иммутабельным?
Примитивы (числа, строки, логические значения, символы, а также null
и
undefined
) всегда иммутабельны.
var foo = 27;
foo.bar = 42;
console.log(foo.bar)
// → undefined
Чтобы сделать иммутабельным объект, используйте Object.freeze()
. Этот
метод существует ещё со времён ES5 и хорошо поддерживается браузерами.
const foo = Object.freeze({
'bar': 27
});
foo.bar = 42; // выбрасывает исключение TypeError в строгом режиме;
// просто не работает в обычном режиме
console.log(foo.bar);
// → 27
Обратите внимание на то, что Object.freeze()
не поддерживает «глубокой
заморозки» — свойства–объекты замороженного объекта всё ещё могут меняться.
В статье на MDN об Object.freeze()
приводится пример реализации функции
deepFreeze()
, делающей объект полностью иммутабельным.
Возможно, иммутабельные структуры данных появятся в будущих версиях ECMAScript — такое предложение уже было внесено.
const
vs. let
Единственное отличие const
от let
состоит в том, что const
предотвращает
повторное присваивание какого-либо значения.
Всё, что написано до этого предложения — факты. Всё написанное после него — моё субъективное мнение, но отнеситесь к нему с терпением.
Учитывая вышесказанное, const
делает код более читабельным: в своей области
видимости const
-переменная всегда ссылается на один и тот же объект. В случае
с let
-переменными такой уверенности быть уже не может. Поэтому будет разумно
использовать let
и const
так:
- используйте
const
по умолчанию - используйте
let
, если потребуется присвоить переменной другое значение - (
var
не должно использоваться в ES6)
Согласны ли вы? Почему (нет)? Мне особенно интересно мнение разработчиков,
предпочитающих в первую очередь использовать let
(даже если значение
присваивается переменной только один раз). Почему вы предпочитаете использовать
let
вместо const
? Это из-за заблуждения «const
для констант» или по другой
причине? Напишите в комментариях!