JavaScript предоставляет методы объекта, которые могут ограничивать изменчивость объекта. Методы preventExtensions(), seal() и freeze() — отличные инструменты, хотя они работают не совсем так, как можно было бы интуитивно ожидать.

Что такое неизменность и зачем она нужна?

Краткий ответ на первый вопрос таков:

Объект является неизменным, когда он не может быть изменен (мутирован).

Но что именно означает «изменился»? Учитывается ли изменение вновь созданного собственного свойства? Да, очевидно. Но считается ли изменение унаследованного свойства в объекте-прототипе «изменением» в дочернем объекте? Это уже не так очевидно. Как видите, все очень быстро усложняется.

Зачем нам вообще нужна неизменность? Причин несколько:

  • После создания состояние неизменяемого объекта не меняется
  • Улучшенная инкапсуляция. Передача объекта какому-либо методу не изменит исходное состояние этого объекта.
  • Возможное упрощение реализации класса/прототипа
  • Доступ к более старым версиям вашего объекта до тех пор, пока вы не удалите его намеренно или не оставите сборщику мусора.

Чтобы понять тему, нам нужно исследовать ее более тщательно, но сначала давайте вспомним несколько важных фактов об объектах в JavaScript.

Как устроены объекты внутри?

Прежде чем мы перейдем к обсуждению неизменности объектов, нам нужно знать, как они устроены внутри. Почему мы должны заботиться? Потому что каждое свойство объекта содержит метаданные, которые описывают, можно ли его изменить. Эти метаданные называются дескриптором свойства.

Расширение, запечатывание и замораживание определяются с точки зрения того, что эти методы делают с дескрипторами свойств объекта.

Определения всех атрибутов дескриптора свойства показаны в таблице ниже.

Не все свойства конкретного объекта являются его собственными. Некоторые из них происходят из прототипа объекта или, строго говоря, из цепочки прототипов . Если есть ссылка на свойство (например, obj.foo), интерпретатор ищет foo в obj. Если он не найден, проверяется прототип obj. Если в этом прототипе также нет foo, проверяется прототип прототипа. В конце этого поиска всегда находится объект с прототипом, равным нулю.

Инструменты для создания неизменяемых объектов

JavaScript предоставляет несколько инструментов в виде методов Object для защиты объектов. Ниже вы можете найти описание каждого. Они упорядочены от наименее ограничительного к наиболее ограничительному.

Object.preventExtensions — предотвращает добавление новых свойств к объекту, хотя свойства можно удалить. Еще одно ограничение: свойства по-прежнему можно добавлять к прототипу объекта или другим объектам, на которые ссылаются. Это означает, что мы запрещаем только локальные расширения, то есть собственные свойства. Если мы расширим прототип, наш базовый объект унаследует от него новые свойства.
В ES6 также есть метод Reflect.preventExtensions, который имеет тот же эффект. Единственное отличие состоит в том, что если в качестве аргумента передается примитив, он не будет преобразован в объект, а вместо этого будет сгенерирован TypeError.

Object.seal — предотвращает добавление к нему новых свойств, а также помечает все существующие свойства как ненастраиваемые. Значения существующих свойств можно изменить, если они доступны для записи.

Object.freeze — предотвращает добавление к нему новых свойств; предотвращает удаление существующих свойств; и предотвращает изменение существующих свойств или их перечислимости, возможности настройки или доступности для записи, а также препятствует созданию прототипа. от того, чтобы быть измененным. Обратите внимание, что значения, которые являются объектами, по-прежнему могут быть изменены, если они также не заморожены.

Чтобы быть постоянным объектом, весь граф ссылок (прямые и косвенные ссылки на другие объекты) должен ссылаться только на неизменяемые замороженные объекты. О замороженном объекте говорят, что он неизменяем, поскольку все состояние объекта (значения и ссылки на другие объекты) внутри всего объекта фиксировано.

Чтобы сделать объект постоянным, рекурсивно заморозьте каждое свойство типа object (глубокая заморозка).

Инструменты для проверки изменчивости объектов

У каждого инструмента для изменения изменчивости объекта есть сопутствующий метод для проверки того, использовался ли он на конкретном объекте.

Object.isExtensible — проверяет, не заблокированы ли расширения в конкретном объекте (может ли он иметь добавленные к нему новые свойства)

Object.isSealed — проверяет, не является ли объект расширяемым и являются ли все его свойства ненастраиваемыми и, следовательно, не удаляемыми (но не обязательно недоступно для записи)

Object.isFrozen — если он не является расширяемым, все его свойства не могут быть настраиваемыми, и все его свойства данных (т. е. свойства, которые не являются свойствами доступа с компонентами получения или установки), являются недоступными для записи.

Внешние инструменты для неизменности объектов

В NPM вы можете найти простые инструменты, которые упростят предотвращение изменений в объектах: например, инструмент для рекурсивной заморозки объектов.



Другой популярный вариант — более абстрактная библиотека Facebook, которая предоставляет готовые к использованию неизменяемые коллекции. Нет необходимости вручную контролировать изменчивость объектов, когда вы можете использовать готовое решение.



*Примечание по ключевому слову const

Ключевое слово ES6 const может ввести в заблуждение в контексте этой статьи. Интуитивно его название предполагает, что оно может иметь что-то общее с неизменностью. Это верно, но речь не идет о неизменности значения. Скорее, речь идет о неизменности связывания переменных. Если вы объявите объект с помощью ключевого слова const, это не сделает его доступным только для чтения. Вместо этого переменная всегда будет указывать на один и тот же объект, и вы не сможете изменить (мутировать) это. Однако вы можете изменить ссылочный объект.

дальнейшее чтение

[1] http://2ality.com/2013/08/protecting-objects.htmlАксель Раушмайер по теме
[2] https://facebook.github.io/immutable- js/ — библиотека с готовыми неизменяемыми структурами данных
[3] https://medium.com/@yej.arin.choi/this-is-a-post-that-summarizes-my -погружение-в-неизменяемость-в-программировании-что-это-почему-это-34cbba44f889

Источники

[4] http://speakingjs.com/es5/
[5] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object< br /> [6] https://developer.mozilla.org/en-US/docs/Glossary/Mutable — описывает, что является изменяемым в JS.