Mutable / Immutable


Array.sort() Array.reverse() Array.splice() 같은 배열 메서드는 원본 배열을 변경한다. 객체(배열) 자체를 변경하면 예상하지 못한 사이드 이펙트가 발생할 수 있다. 아래 코드에서 reversed는 결과적으로 regions 배열을 가리키고 있다. 즉, regions reversed 이 둘은 동일하다.

const regions = ['Seoul', 'Shanghai', 'Tokyo'];
const reversed = regions.reverse();
console.log(regions); // ['Tokyo', 'Shanghai', 'Seoul']
console.log(reversed); // ['Tokyo', 'Shanghai', 'Seoul']

// 두 값이 동일한지 여부, 비교하는 값이 객체일 땐 참조값 비교
Object.is(regions, reversed); // true

<aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" /> Immer 같은 라이브러리를 사용해서 불변성을 유지할 수도 있다.

</aside>

한편, 리액트에서 불변성을 지키지 않고 객체를 변경한 후 상태를 설정하면 리렌더링이 발생하지 않는다. 이러한 이유로 리액트 공식 문서에선 push splice reverse 같은 mutable 메서드 대신, concat 전개연산자 filter slice map 같은 immutable 메서드 사용을 권장하고 있다.

Array.from() 전개 연산자 slice() 등을 통해 배열 복사본을 만들고 작업을 수행하면 불변성을 지킬 수 있지만, 매번 배열 복사본을 만드는건 번거로운 일이다.

ES2023에 새로 공개한 toSorted toReversed toSpliced with 메서드는 원본 배열을 복사하고, 복사본에 대해 변경을 수행한다. 따로 배열 복사본을 만들지 않아도 돼서 가독성도 좋아진다.

ES2023 배열 메서드


아래 ES2023 배열 메서드는 바이너리 데이터를 다룰 때 사용하는 TypedArray에도 사용할 수 있다. (TypedArray엔 splice 메서드가 없으므로 toSpliced 메서드는 사용 불가)

Array.toSorted

Array.toSorted() 메서드는 기본적으로 Array.sort() 메서드와 동일하지만 새로운 배열을 반환하는 점이 다르다. 원본 배열에는 아무런 영향을 미치지 않기 때문에 사이드 이펙트 없이 순수 함수처럼 동작한다.

const numbers = [1, 2, 3, 4, 5];
const descNumbers = numbers.sort((a, b) => b - a); // 원본 배열 변경
Object.is(numbers, descNumbers); // true
console.log(numbers); // [5, 4, 3, 2, 1]
console.log(descNumbers); // [5, 4, 3, 2, 1]
const numbers = [1, 2, 3, 4, 5];
const descNumbers = numbers.toSorted((a, b) => b - a); // 복사본 생성
Object.is(numbers, descNumbers); // false
console.log(numbers); // [1, 2, 3, 4, 5]
console.log(descNumbers); // [5, 4, 3, 2, 1]

Array.toReversed

Array.toReversed() 메서드는 Array.reverse() 메서드와 유사하게 동작하지만, 원본 배열을 변경하지 않고 순서가 반전된 새로운 배열을 반환한다.

const numbers = [1, 2, 3, 4, 5];
const descNumbers = numbers.reverse(); // 원본 배열 변경
Object.is(numbers, descNumbers); // true
console.log(numbers); // [5, 4, 3, 2, 1]
console.log(descNumbers); // [5, 4, 3, 2, 1]
const numbers = [1, 2, 3, 4, 5];
const descNumbers = numbers.toReversed(); // 복사본 생성
Object.is(numbers, descNumbers); // false
console.log(numbers); // [1, 2, 3, 4, 5]
console.log(descNumbers); // [5, 4, 3, 2, 1]