개발 공부/Translation

[번역] ES2023 introduces new array copying methods to JavaScript

Ryomi 2023. 11. 3. 14:11
728x90
반응형

 

최근 ECMAScript 2023 Specification이 확정되었다. 이는 자바스크립트 프로그램을 더 예측가능하고 유지관리할 수 있게 하는 배열 method를 포함한다. toSorted(), toReversed(), toSpliced(), with()는 data를 변경하지 않고 data 복사 후 이를 변경함으로써 배열에 작업을 수행할 수 있게 한다. 그 차이 당신의 프로젝트에 이들을 적용하는 방법을 배워보자.

 

Mutation and Side effects

배열은 항상 다소 기이하다. sort(), reverse(), splice()와 같은 method는 배열 그 자체를 변경한다. cancat(), map(), filter()와 같은 다른 method는 배열을 복사한 후 그 배열을 조작한다. 배열을 변경시키는 조작을 한다면 이는 side effect이며 당신의 시스템 곳곳에 예상치 못한 동작을 유발할 수 있다. 

 

예시에서 처럼, 이러한 현상은 배열을 역순으로 배열할 때 발생한다.

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = languages.reverse();
console.log(reversed);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(languages);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(Object.is(languages, reversed));
// => true

 

보다시피, 원본 배열은 역순으로 배열되었고 새로운 변수에 배열을 역순으로 배열한 결과를 할당했지만, 두 변수는 모두 같은 배열을 가리키고 있다. 

 

 

Mutating arrays and React

배열을 변경시키는 method가 가진 가장 잘 알려진 문제는 당신이 React component에 이들을 사용할 때 이다. 배열 그 자체는 동일한 object이고 이는 rerendering을 유발하지 않으므로 우리는 배열을 변경할 수 없으므로 새로운 상태로 설정하려고 할 것 이다.  먼저 배열을 복사한 후 이를 조작하고 새로운 상태로 설정할 필요가 있다. React 문서는 이때문에 한 페이지 전체에 상태에서 배열을 업데이트하는 방법을 설명하고 있다. 

 

 

Copy fist, then mutate

이를 해결하는 방법은 먼저 배열을 복사하고나서 이를 수정하는것이다. 배열을 복사하는 여러 방법이 있다 - Array.from(), spread operator, 인자가 없는 slice()을 사용해 배열을 복사할 수 있다. 

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = Array.from(languages).reverse();
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
console.log(Object.is(languages, reversed));
// => false

 

해결방법이 있다는것은 좋지만 여러 copy method 중 하나를 수행해야함을 기억해야만 하는건 좋지 않다.  

 

 

New methos change by copy

이것이 새로운 method가 등장한 이유이야. toSorted, toReversed(), toSpliced(), with()는 원본배열을 복사한 후 이를 변경한 후 반환한다. 당신은 오직 하나의 함수만 기억하면 되므로 쓰기 쉽고 먼저 배열을 복사하는 네가지 method 중 하나를 분석할 필요가 없으므로 읽기 쉽게 만들 수 있다. 그럼 각각의 method는 무슨 역할을 할까?

 

 

Array.proptotype.toSorted

toSorted()는 새로운 정렬된 배열을 반환한다. 

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const sorted = languages.toSorted();
console.log(sorted);
// => [ 'CoffeeScript', 'JavaScript', 'TypeScript' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]

sort()는 복사 이외에 몇몇의 예상치 못한 동작을 하며 toSorted() 역시 그렇다. 숫자나 악센트가 있는 문자열을 정렬할 때 여전히 주의를 기울여야 한다. 당신이 원하는 결과를 만들어내는, string의 localeCompare과 같은 비교함수를 제공하는지 확인해라.

 

const numbers = [5, 3, 10, 7, 1];
const sorted = numbers.toSorted();
console.log(sorted);
// => [ 1, 10, 3, 5, 7 ]
const sortedCorrectly = numbers.toSorted((a, b) => a - b);
console.log(sortedCorrectly);
// => [ 1, 3, 5, 7, 10 ]

const strings = ["abc", "äbc", "def"];
const sorted = strings.toSorted();
console.log(sorted);
// => [ 'abc', 'def', 'äbc' ]
const sortedCorrectly = strings.toSorted((a, b) => a.localeCompare(b));
console.log(sortedCorrectly);
// => [ 'abc', 'äbc', 'def' ]

 

 

Array.prototype.toReversed

toReversed()를 사용하면 역순으로 정렬된 새로운 배열을 반환한다.

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = languages.toReversed();
console.log(reversed);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]

Sonar는 reverse()과 같은 method의 잘못된 사용을 다루는 규칙을 가지고 있다. 새로운 변수에 reverse()의 결과를 할당하는 것은 원본배열 역시 변경하므로 잘못되었다. 이제 당신은 배열을  toReversed나 toSorted()를 사용해 배열을 복사하고 변경할 수 있다. 

 

 

Array.prototype.toSpliced

toSpliced()는 original version인 splice()와는 다소 차이가 있다. splice는 기존의 배열에서 제공된 index의 요소를 삭제하고 추가함으로써 변경하고 배열에서 삭제된 요소를 포함한 배열을 반환한다. toSpliced는 제거된 요소 없이 요소들이 추가된 새로운 배열을 반환한다. 여기 toSpliced()가 어떻게 동작하는지 나와있다. 

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const spliced = languages.toSpliced(2, 1, "Dart", "WebAssembly");
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]

 

만약 당신이 return value로 splice를 사용한다면 toSpliced()는  splice()의 대체제가 아닐것이다. 만약 원본 배열을 변경하지 않고 삭제된 요소를 알고싶다면 그럼 당신은 copy method인 slice()를 사용해야한다. 

안타깝게도 splice는 다른 인자를 받는다.  splice()는 하나의 index를 받고 index 이후로 제거할 요소의 갯수를 받으며 2개의 index를 받는다. 만약 당신이 splice() 자리에 toSpliced를 사용하길 원하지만 삭제된 요소를 얻길 원한다면 당신은 원본배열에 toSpliced와 slice를 사용할 수 있다. 

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const startDeletingAt = 2;
const deleteCount = 1;
const spliced = languages.toSpliced(startDeletingAt, deleteCount, "Dart", "WebAssembly");
const removed = languages.slice(startDeletingAt, startDeletingAt + deleteCount);
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]
console.log(removed);
// => [ 'CoffeeScript' ]

 

 

Array.prototype.with

 with 함수는 배열에서 하나의 요소를 바꾸기 위해 square bracket notion을 사용한 복사와 동일하다. 그래서 아래 예시처럼 배열을 직접 바꾸는 대신 :

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
languages[2] = "WebAssembly";
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]

 

배열을 복사하고 변경할 수 있다. 

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const updated = languages.with(2, "WebAssembly");
console.log(updated);
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', CoffeeScript' ]

 

 

Not just arrays

보통의 배열만이 이 새로운 method들로 이점을 취하진 않는다. 당신은 또한 TypedArray에 toSorted(), toReversed(), with()를 사용할 수 있다. Int8Array 부터 BigUnit64Array 까지 모두이다. TypedArray는splice() method가 없으므로 toSpliced method와 매칭되지 않는다.

 

 

Caveats

map, filter, concat과 같은 method들은 이미 복사 동작을 수행한다고 앞서 언급했다. 만약 배열을 확장하고 map, flatMap, filter, concat을 instance에 사용한다면 이는 동일한 타입의 새로운 instance를 반환할 것이다. 만약 배열을 확장하고 toSorted, toReversed, toSpliced, with를 사용한다면 결과는 다시 순수한 배열일 것이다. 

 

from()을 사용해 이를 다시 custom 배열로 바꿀 수 있다. 

class MyArray extends Array {}
const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");
const upcase = languages.map(language => language.toUpperCase());
console.log(upcase instanceof MyArray);
// => true
const reversed = languages.toReversed();
console.log(reversed instanceof MyArray);
// => false

 

 

Support

ECMAScript 2023 spec은 매우 새로운 반면 이미 이 새로운 배열 method에 대한 좋은 지원이 있다. Chrom 110, Safari 16.3, Node.js20, Deno 1.31 모두 4개의 method를 지원하며 아직 지원하지 않는 플랫폼에 대한 폴리필과 shim이 있다.

class MyArray extends Array {}
const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");
const reversed = MyArray.from(languages.toReversed());
console.log(reversed instance of MyArray);
// => true

 

 

 

refence)

 

ES2023 introduces new array copying methods to JavaScript

ES2023 is introducing new array methods in JavaScript & they are here to make our programs more predictable and maintainable by copying instead of mutating.

www.sonarsource.com

 

728x90
반응형