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

II.Почему неизменность

III.Как реализовать неизменяемость

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

Неизменяемость означает, что однажды созданные данные не могут быть изменены.

ex: const age= 25;
age = age + 1 // error

II. Почему неизменность

Предсказуемость

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

let number = 5;
number = “Linda”; // js is a loosely typed language. 

Производительность

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

Чтобы узнать больше о структурном разделении: https://hackernoon.com/how-immutable-data-structures-e-g-immutable-js-are-optimized-using-structural-sharing-e4424a866d56

Отслеживание мутаций

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

III. Как реализовать неизменяемость

1. Использование правильных конструкций es6 / языка

const, оператор распространения, присвоение, замораживание, фрагмент, concat, JSON.stringify () / parse (), карта, фильтр, сокращение

sort, reverse, push и pop изменяют исходный массив.

2. Функциональное программирование

3. Использование неизменяемой библиотеки

immutable.js - реализация Facebook с JS-подобным API.

• Mori - постоянные структуры данных ClojureScript, экспортируемые в JS.

старинный дуб

1. Использование правильных конструкций es6 / языка

Оператор распространения (…): использование оператора распространения для массивов и объектов.

const business_days = [‘Mon’, ‘Tue’, ‘Wed’, ‘Thu’, ‘Fri’];
const week_ends = [‘Sat’, ‘Sun’];
const days = […business_days, …week_ends];
console.log(“days:”, days); //[“Mon”, ”Tue”, ”Wed”, ”Thu”, ”Fri”, ”Sat”, ”Sun”];
  • Используйте оператор распространения в качестве альтернативы push (), который изменяет исходный массив.

Object.freeze (): этот метод замораживает объект. Замороженный объект больше нельзя изменить. Это предотвращает добавление к нему новых свойств и удаление существующих свойств. Это также предотвращает изменение его прототипа.

const person = {
name: ‘John’
};
person.name = ‘Linda’
person.gender = ‘female’;
console.log (person);
Object.freeze(person);
person.name = ‘Smith’; 
person.gender = ‘Male’;
console.log (person); // changes made after freeze will not be applied as the object is frozen.

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

Object.assign (): копирует значения из одного или нескольких исходных объектов в целевой объект. Он имеет подпись Object.assign (цель,… источники).

• Объединить объект

let first = {name: ‘Linda’};
let last = {lastName: ‘Smith’};
let fullName = Object.assign(first, last);
console.log(fullName);

• Клонировать объект

let obj = {person: ‘Thor Odinson’};
let clone = Object.assign({}, obj);
console.log(clone);

slice (): метод slice () возвращает выбранные элементы в массиве как новый объект массива.

var fruits = [“Banana”, “Orange”, “Lemon”, “Apple”, “Mango”];
var citrus = fruits.slice(1, 3);
console.log(fruits); // [“Banana”, “Orange”, “Lemon”, “Apple”, “Mango”]
console.log(citrus); // [“Orange”, “Lemon”]

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

console.log(fruits.slice().reverse()); //same is applicable to sort, push and pop

Concat (): метод concat () используется для объединения двух или более массивов. Этот метод не изменяет существующие массивы, но возвращает новый массив, содержащий значения объединенных массивов.

const array1 = [‘a’, ‘b’, ‘c’];
const array2 = [‘d’, ‘e’, ‘f’];
console.log(array1.concat(array2)); // [“a”, “b”, “c”, “d”, “e”, “f”]
console.log(array1); // [“a”, “b”, “c”]
console.log(array2); // [“d”, “e”, “f”]

JSON.parse () и JSON.stringify (): еще один способ скопировать объект - использовать JSON.stringify и выполнить синтаксический анализ без изменения исходного объекта.

var obj1 = {
sandwich: ‘turkey’,
soda: ‘Pepsi’,
chips: ‘Cape Cod’
};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.cookie = ‘chocolate chip’; //Add an item to object 2
console.log(obj1); //logs {sandwich: “turkey”, soda: “Pepsi”, chips: “Cape Cod”}
console.log(obj2); // logs {sandwich: “turkey”, soda: “Pepsi”, chips: “Cape Cod”, cookie: “chocolate chip”}

Map (), filter (), every (), some () и reduce ():

const numbers = [1,2,3,4,5,6,7];

// Map () - отображает значения, применяя условие без изменения исходного массива

const doubleNumbers = numbers.map(x => x* 2);

// Фильтр () - фильтрует значения массива без изменения исходного массива

const evenNumbers = numbers.filter(x => x%2 === 0);

// Некоторые - возвращает истину, если хотя бы один элемент удовлетворяет заданному условию.

const result = employees.some(employee => employee.sal > 500,000)

// Уменьшить - принимает массив данных и сводит к одному значению

const numbers = [1,2,3,4,5];
const sum = numbers.reduce((acc, number) => {
return acc+ number;
}, 0)
console.log(sum); //15

2. Функциональное программирование:

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

Плюсы:

• Декларативная

• Сосредоточен на том, «что»

• Чистые функции без побочных эффектов

• Неизменяемость

• Легко тестируемый код

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

• Первоклассные функции

• Функции высшего порядка

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

Традиционный подход:

function add(arg1, arg2) {
   return arg1 + arg2;
}
const addNumbers = function(arg1, arg2) {
   return arg1 + arg2;
}

Функциональный подход:

myFunction = (arg1, arg2) => arg1 + arg2;

Первоклассные функции: функции, возвращающие другую функцию (создатели функций).

const double = x => x *2;
const triple = x => x * 3;
const quadruple = x=> x * 4;

Вышеупомянутые 3 функции могут быть переписаны с использованием первоклассных функций.

const createMultiplier = y => x => x * y
const double = createMultiplier(2);
const triple = createMultiplier(3);
const quadruple = createMultiplier(4);
console.log(double(3));

Функции высшего порядка. Функции высшего порядка принимают функцию в качестве аргумента и возвращают функции.

const divide = (x,y) => x / y;
const secondArgumentIsntZero = func =>
    (…args) => {
       if (args[1] === 0) {
          console.log(“Error: dividing by zero’);
          return null;
       }
   return func(…args);
}
const divideSafe = secondArgumentIsntZero(divide);// function is sent as an argument.
console.log(divideSafe(7,0));

3. Использование библиотеки Immutability.js:

• Гарантированная неизменяемость. Данные, инкапсулированные в объекте Immutable.JS, никогда не изменяются. Всегда возвращается новая копия.

• Богатый API - Immutable.JS предоставляет богатый набор неизменяемых объектов для инкапсуляции ваших данных - Карты, Списки, Наборы, Записи

• Производительность - Immutable.JS выполняет много негласной работы, чтобы оптимизировать производительность, сводя к минимуму необходимость копирования данных.

• Минусы: вы больше не сможете ссылаться на свойства объекта с помощью стандартной точки или скобки JavaScript. Вместо этого вы должны ссылаться на них через Immutable.JS get ()

• Невозможно использовать операторы распространения.

Пример неизменяемой структуры данных

Ссылка: https://immutable-js.github.io/immutable-js/

Образец кода:

•Список:

const list = Immutable.List([‘Hello’]);
const updatedList = list.push(‘World’);
console.log(list.toJS());
console.log(updatedList.toJS());

•Записывать:

const Person = Immutable.Record({name: ‘John’, age: 42});
const bob = new Person({name: ‘bob’});
console.log(bob.toJS());

•Карта

const map = Immutable.Map();
const updatedMap = map.set(‘first’, ‘1’)
.set(‘second’, ‘2’);
const mergedMap = updatedMap.merge({first: ‘11’, third: ‘3’});
console.log(“original map:”, map.toJS());
console.log(“updated map:”,updatedMap.toJS());
console.log(“merged map:”,mergedMap.toJS());
mergedMap.forEach((value) => {
  console.log(value);
});

Другие способы повышения неизменяемости:

Использование плагинов esLint для реагирования приложений во избежание случайной мутации: плагины esLint помогают напоминать о случайных мутациях. Установите eslint-plugin-immutable как зависимость разработчика для отслеживания мутаций

›Npm install - save-dev eslint-plugin-immutable