Что на самом деле означает неизменность?

и как это применяется в JavaScript?

Концепция неизменности - забавная.

В одном переводе это может означать неизменный - однако установка чего-либо как let или var и затем запрет на изменение может быть так же хорош, как и использование const

Многие люди заходят слишком далеко в применении этого перевода и в результате неправильно понимают концепцию и глубину неизменяемости.

В этой статье я расскажу об идее неизменяемости и о том, как она применяется в JavaScript.

Давайте посмотрим на значение неизменности

Слово неизменяемость происходит от латинского языка и означает «неизменяемый». Его официальный синоним - «неизменный», и применительно к компьютерному программированию идея почти такая же.

Однако это приложение часто упрощается. Многие разработчики связывают неизменность с невозможностью вообще что-либо изменить.

Например:

var cat = "Tibbers";
cat = "Tibbles";

В соответствии с неправильным применением концепции приведенный выше пример рассматривается как нарушение неизменности.

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

Разработчики часто путаются, потому что, если неизменяемость означает «ничего не менять», то почему бы просто не использовать const?

Этот скептицизм обоснован, и я долгое время подвергал сомнению и эту логику.

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

Когда дело доходит до объектно-ориентированного и функционального программирования, концепция неизменяемости применяется на уровне объекта.

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

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

Итак, если бы мы сделали шаг назад и понаблюдаем за состоянием, которое есть, по сути, это будет «форма» и «внешний вид» определенного объекта в любой данный момент времени. Это означает, что при добавлении или удалении свойств изменяется общая форма и, следовательно, состояние.

И когда такое изменение может произойти, объект считается mutable

Путаница с неизменностью часто начинается здесь. Это потому, что существуют разные уровни применения. Есть приложения на переменном уровне - где используется const.

Слабая и сильная неизменяемость - настоящая вещь

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

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

Напротив, полное замораживание объекта - это жесткое и сильное применение неизменности.

Это отличается от const.

Как? const неизменен с самого начала. Он также часто ассоциируется и применяется к примитивам, таким как String и Boolean. В момент назначения - вот и все - в нем ничего не может измениться.

Это удобно для таких вещей, как точные и быстрые значения, такие как pi и система преобразования измерений.

Однако объекты созданы изменчивыми по своей природе. Ей можно придавать форму и формовать до тех пор, пока разработчик не будет достаточно удовлетворен, чтобы превратить его в неизменный объект.

Подумайте об этом так: изменчивый объект подобен мокрой глине. Вы можете придать ему любую форму. Как только вы поместите его в печь, форма станет неизменной.

Однако вы все еще можете его раскрасить. Внешний вид еще может измениться.

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

Неизменяемость в JavaScript

В JavaScript все примитивные типы изначально слабо неизменяемы.

Почему?

Потому что форма undefined, null, boolean, number, bigInt, String и Symbol единственная и плоская. Это однократное присвоение, и когда инициализируется примитивный тип, есть четкое представление о том, как он будет выглядеть.

Если вы измените присвоенное значение, он не сжимается, не расширяется или не делает забавных вещей. Он может изменить ваш обработанный вывод при входе в функцию, но это другая история.

Если вам нужно, чтобы он был строго неизменяемым, используйте const

В JavaScript пользовательские объекты очень изменчивы - если вы явно не укажете, что это не так.

Как ты делаешь это? Вы можете сделать это через Object.defineProperty() или Object.freeze()

В нормальных и очень mutable обстоятельствах вы можете сделать это:

let cat = {};
cat.name = "Tibbers";
cat.legs = 4;
console.log(cat);
delete cat.legs;
cat.wings = 2;
console.log(cat);

В приведенном выше примере вы можете продолжать добавлять и удалять свойства, изменять значения влево, вправо и по центру - и ничто не остановит вас или мошеннический код, который вы случайно создаете в далеком-далеком будущем.

В конце концов, ваш cat объект будет больше похож на птицу, чем на настоящее существо, которое мяукает.

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

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

Object.defineProperty ()

Принцип работы Object.defineProperty() заключается в том, что он принимает три параметра: объект, над которым вы хотите работать, свойство, с которым вы имеете дело, и значения дескриптора.

Вкратце, Object.defineProperty() позволяет вам определять и устанавливать статус изменяемости конкретного объекта.

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

В кодовой форме это выглядит примерно так:

let teaPot = {};
Object.defineProperty(
   teaPot, 
   'color', 
   {value: 'blue', writable: false}
);
console.log(teaPot);

Когда вы console.log() это выпустите, вы можете ожидать чего-то вроде этого:

{ color: "blue" }

В Object.defineProperty() есть еще кое-что, но в настоящее время это выходит за рамки данной статьи. Однако этого будет достаточно, чтобы вы начали путь к неизменности и уменьшению дефектов (то есть неожиданных изменений) из-за мутаций значений.

Однако вы должны принять во внимание, что он не полностью устанавливает все состояние объекта. Вы все еще можете добавить дополнительные свойства к объекту teaPot. Единственное, что вы не можете изменить то, что уже было установлено как недоступное для записи.

Даже если вы запустите что-то вроде delete teaPot.color, как только будет установлено значение, запрещающее запись, ничто не сможет избавиться от него с помощью обычных методологий.

Так, например, эта частичная реализация строгой неизменяемости переменных, но изменяемого состояния в JavaScript все равно будет выполняться. delete будет игнорироваться.

let teaPot = {};
Object.defineProperty(
   teaPot, 
   'color', 
   {value: 'blue', writable: false}
);
teaPot.age = 3;
teaPot.age = 4;
teaPot.color = 'green';
delete teaPot.color;
console.log(teaPot);

console.log выведет - {age: 4, color: "blue"}

В результате color можно рассматривать как сильное неизменяемое свойство, в то время как сам объект остается изменяемым.

Так как же предотвратить полное изменение объекта? Вы можете использовать Object.freeze()

Object.freeze ()

Object.freeze() позволяет вам установить состояние объекта с неизменяемостью. По сути, он «замораживает» ваш объект в его текущем состоянии, и любые попытки изменить его приводят к игнорированию вредоносного кода.

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

Это особенно хорошо для использования в сочетании с Object.defineProperty(), потому что это позволяет вам создавать объект с учетом неизменности и в качестве конечной цели.

Например, у вас может быть несколько свойств, которые изменяются в рамках определенного набора процессов, но их необходимо настроить и согласовать с тем, что не изменится после подтверждения. В любом случае, вот пример того, как Object.freeze() может использоваться вместе с Object.defineProperty() на основе предыдущих примеров выше.

let teaPot = {};
Object.defineProperty(
   teaPot, 
   'color', 
   {value: 'blue', writable: false}
);
teaPot.age = 3;
teaPot.age = 4;
teaPot.color = 'purple';
Object.freeze(teaPot);
teaPot.color = 'green';
teaPot.age = 5;
delete teaPot.color;
console.log(teaPot);

В результате получится следующий объект:

{age: 4, color: "blue"}

Я знаю, что это не лучший пример, но вы уловили суть.

Последние мысли

В простейшем и наиболее слабом виде неизменяемость применяется на уровне shape.

Сильная неизменность - это когда вещи просто неизменны.

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

В тот момент, когда была проявлена ​​фотография, изображение становится тем, чем оно является. Чтобы внести изменения, вам нужно будет сделать физическую копию, прежде чем с ней что-нибудь можно будет сделать. Изменение оригинала по существу уничтожит его, а сам объект будет навсегда потерян.

Теперь возникает вопрос: а что, если объект нужно изменить? Иногда такое случается, и это нормально.

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

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

И на этом все в рамках этой статьи.

Спасибо за чтение.