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

Ответ на этот вопрос до сих пор ускользает от нас.

Однако, к счастью для всех нас, это привело к интересным дискуссиям о JavaScript, Array.map и о том, что, черт возьми, происходит.

Первый: мутировать проклятый объект.

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

примером этого может быть:

const originalComplexArray = [{ value: 'To be changed' }];
const newComplexArray = originalComplexArray.map(myObject => {
   myObject.value = 'Changed value';
   return myObject;
});

Используя Жасмин, вы можете увидеть утверждение, которое это доказывает.

// both arrays contain the same object.
expect(newComplexArray[0]).toBe(originalComplexArray[0]);
// both objects have the same values and properties...
expect(newComplexArray[0]).toEqual(originalComplexArray[0]);
// array's are different arrays
expect(newComplexArray).not.toBe(originalComplexArray);
// both arrays have the exact same values
expect(newComplexArray).toEqual(originalComplexArray);

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

Два: Object.assign

Если мы хотим обеспечить неизменность объектов в массиве. Один из способов сделать это - использовать Object.assign.

const originalComplexArray = [{ value: ‘To be changed’ }];
const newComplexArray = originalComplexArray.map(myObject => {
// return new object with the value changed. Original is not mutated
return Object.assign({}, { value: ‘Changed value’ });
});

Опять же, используя утверждения Жасмин, мы можем это доказать.

// not the same object reference
expect(newComplexArray[0]).not.toBe(originalComplexArray[0]);
// objects have the same properties with different values
expect(newComplexArray[0]).not.toEqual(originalComplexArray[0]);
// array's are different arrays
expect(newComplexArray).not.toBe(originalComplexArray);
// the original arrays values do not match our new array. the original array is unchanged
expect(newComplexArray).not.toEqual(originalComplexArray);

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

Три: оператор спреда.

TypeScript и скоро в ECMAScript предоставляют оператор распространения , который позволяет нам создавать новые объекты (и, кстати, массивы) путем деконструкции старого. Это позволяет нам достичь того же результата, что и Object.assign, но в более удобочитаемом формате.

const originalComplexArray = [{ value: 'To be changed' }];
const newComplexArray = originalComplexArray.map(myObject => {
   // return new object with the value changed. Original is not       mutated
   return { ...myObject, value: 'Changed value' }
});

Но подождите, что, если по какой-то причине вы хотите изменить исходные значения массива, но не имеете проблем со ссылками, созданных простым изменением объекта в map? В этом случае вы можете использовать оператор распространения ПОСЛЕ того, как вы изменили исходное значение.

const originalComplexArray = [{ value: 'To be changed' }];
const newComplexArray = originalComplexArray.map(myObject => {
    // mutates the original object in the array.
    myObject.value = 'Changed value';
    // returns new object created from the mutated object
    return { ...myObject }
});

Вот тесты, которые показывают, что здесь происходит

// not the same object reference
expect(newComplexArray[0]).not.toBe(originalComplexArray[0]);
// objects have the same properties with the same values
expect(newComplexArray[0]).toEqual(originalComplexArray[0]);
// array's are different arrays
expect(newComplexArray).not.toBe(originalComplexArray);
// both arrays have the exact same values
expect(newComplexArray).toEqual(originalComplexArray);

Как видите, все зависит от того, как вы изменяете свой объект в массиве. И в зависимости от того, как вы структурируете свои компоненты, обнаружение изменений Angular может или не может сработать. Поэтому важно знать, как использовать Array.map для изменения значений сложных массивов.

Если вам понравилась эта статья, пожалуйста, хлопните в ладоши.

Ссылка на содержание тестов GitHub