ES2020, ES2021에 추가된 기능

Bard·2021년 7월 18일
38
post-thumbnail

본 글은 Alberto Montalesi의 Complete Guide to Modern JavaScript 를 참고한 포스팅입니다. 오타, 피드백 등은 답글을 달아주세요!

ES2020의 새로운 기능

1. BigInt

BigInt에 대한 지원은 자바스크립트에서 매우 큰 정수를 저장할 수 있음을 의미한다. 현재 정수의 최댓값은
25312^{53} -1 이며, Number.MAX_SAFE_INTEGER를 사용하여 얻을 수 있다. 그렇다고 더 큰 정수를 저장할 수 없다는 의미는 아니지만 자바스크립트는 이를 제대로 처리하지 못한다.

let num = Number.MAX_SAFE_INTEGER
//9007199254740991
num + 1;
//9007199254740992
num + 2;
//9007199254740992
num + 3;
//9007199254740994
num + 4;
//9007199254740996

보다시피 자바스크립트가 처리할 수 있는 최댓값에 도달하면 제대로 된 결과가 나오지 않기 시작한다.

BigInt를 사용하려면 BigInt 생성자를 사용하거나 큰 정수 뒤에 n을 붙이면 된다.

let bigInt = BigInt(99999999999999999);

let bigInt = 99999999999999999n;
bigInt + 1n;
//100000000000000000n

BigInt에 1을 더하려면 먼저 parseInt(bigInt, 1)을 사용하여 BigIntint로 변경해야 한다,. 이 예시 에서는 이 번거로움을 피하기 위해 1을 더하는 대신 1n을 더했다.

2. 동적으로 가져오기

ES2020부터는 필요할 때 모듈을 동적으로 가져올 수 있다. 다음 예를 살펴보자.

if(condition1 && condition2) {
  const module = await import('./path/to/module.js');
  module.doSomething();
}

이 코드처럼 런타임에서 모듈이 필요한지 여부를 판단해서, 필요한 경우에만 async/await을 사용하여 해당 모듈을 가져오는 게 가능해졌다.

3. 옵셔널 체이닝

사용자를 나타내는 간단한 객체를 예로 살펴보자

const user1 = {
  name: 'Alberto',
  age: 27,
  work: {
    title: 'software developer',
    location: 'Vietnam',
  },
};

const user2 = {
  name: 'Tom',
  age: 27,
}

이런 예에서 사용자의 직함title^{title}에 접근하고 싶다고 가정해보자. 예제에서 workuser 객체의 선택적 속성이므로 예전에는 다음과 같이 작성해야 했다.

let jobTitle;
if(user.work) {
  jobTitle = user.work.title;
}

혹은 다음과 같이 삼항연산자를 사용하는 방법도 있다.

const jobTitle = user.work ? user.work.title : '';

worktitle속성에 접근하기 전에 user 객체가 실제로 work 속성을 가지고 있는지 확인해야 했다는 뜻이다.

단순한 객체를 다룰 때는 그렇게 큰 문제가 아니지만, 접근하려는 속성이 깊게 중첩되어 있는 객체의 경우에는 코드가 많이 복잡해진다.

이때 옵셔널 체이닝optionalchaining^{optional\, chaining} 연산자인 ?.을 사용하면 간결하게 코드를 작성할 수 있다. 옵셔널 체이닝을 이용하여 앞의 코드를 다시 작성해보자.

const jobTitle = user.work?.title

훨씬 간결하고 읽기 좋아졌다.

코드를 보면 user 객체가 work 속성의 존재 여부를 묻는 것처럼 읽히고, 존재한다면 title 속성에 자연스럽게 접근할 수 있다.

const user1JobTitle = user1.work?.title;
// software developer
const user2JobTitle = user2.work?.title;
// undefined

객체에서 사용할 수 없는 속성에 도달하면 연산자는 undefined를 반환한다.

4. Promise.allSettled()

ES6에서는 주어진 모든 프로미스가성공할 때까지 기다릴 수 있는 Promise.all()이 추가되었다. ES2020에 도입된 Promise.allSettled()는 한 단계 더 나아가 성공/실패 여부와 무관하게 모든 프로미스들이 완료될 때까지 기다렸다가 각각의 결과를 설명하는 객체 배열을 반환한다.

이를 통해서 어떤 프로미스가 실패했는지 쉽게 알 수 있다.

const arrayOfPromises = [
  new Promise((res,rej) => setTimeout(res,1000)),
  new Promise((res,rej) => setTimeout(res,1000)),
  new Promise((res,rej) => setTimeout(res,1000)),
];
Promise.allSettled(arrayOfPromises).then(data => console.log(data));

//[
//  {status: "fulfilled", value: undefined},
//  {status: "rejected", value: undefined},
//  {status: "fulfilled", value: undefined},
//]

5. null 계열의 값을 병합하기

거짓값과 null 계열의nullish^{nullish}값(null 또는 undefined)은 떄때로 비슷할 수 있지만 엄연히 서로 다른 값이다. 새로 도입된 연산자를 사용하면 null계열의 값과 거짓값을 서로 구분할 수 있다.

먼저 거짓 값에 대한 복습으로 다음 예제를 살펴보자.

// !! 연산자를 사용해 다양한 자료형의 값을 Boolean으로 변환
const str = "";
console.log(!!str);
//false
const num = 0;
console.log(!!num);
//false
const n = null;
console.log(!!n);
//false
const u = undefined;
console.log(!!u);
//false

모든 출력 값이 거짓으로 나왔다. 이런 경우 빈 문자열과 undefined를 구별하고 싶다면 null 병합 연산자nullishcoalescingoperator^{nullish\, coalescing\, operator}(??)가 유용하다.

null 병합 연산자는 왼쪽 피연산자가 null 계열의 값인 경우 오른쪽 피연산자를 반환환다.

const x = '' ?? 'empty string';
console.log(x);
// ''
const num = 0 ?? 'zero';
console.log(num);
// 0
const n = null ?? "it's null";
console.log(n);
// it's null
const u = undefined ?? "it's undefined";
console.log(u);
// it's undefined

이 예제를 보면 처음 두 예시의 값은 null이 아니라 거짓이므로 연산자가 오른쪽 값을 반환하지 않았다.

6. String.prototype.matchAll()

matchAll() 메서드는 지정된 정규식에 대해 문자열과 일치하는 모든 결과의 반복자를 반환하는 새로운 메서드이다.

// 'a'에서 'd' 사이에 있는 문자를 매칭하기 위한 정규식
const regEx = /[a-d]/g;
const str = "Lorem ipsum dolor sit amet";
const regExIterator = str.matchAll(regEx);

console.log(Array.from(regExIterator));
//[
//  ["d", index: 12, input: "Lorem ipsum dolor sit amet", groups: undefind],
//  ["a", index: 22, input: "Lorem ipsum dolor sit amet", groups: undefind],
//]

위 예제에서는 주어진 문자열에 대해 matchAll() 메서드를 이용하여 a에서 d 범위의 문자에 대해 매칭을 진행했고 두 개의 결과를 얻었다.

7. 모듈 네임스페이스 export 문법

ES2020 이전에도 다음과 같이 import를 할 수는 있었다.

import * as stuff from './test.mjs';

ES2020부터는 export시에도 동일하게 할 수 있다.

export * as stuff from './test.mjs';

이 코드는 다음 코드와 동일한 역할을 수행한다.

export { stuff };

대단한 기능은 아니지만 importexport 문법을 대칭적으로 사용할 수 있게 됐다는 점에 의미가 있다.

8. import.meta

import.meta 객체는 URL 등 모듈에 대한 정보를 노출한다.

<script type="module" src="test.js"></script>
console.log(import.meta); // {url: "file://home/user/test.js"}

객체에 포함된 URL은 인라인 스크립트의 문서 주소일 수도 있고 외부 스크립트를 가져온 URL일 수도 있다.

9. globalThis

자바스크립트에서는 ES2020 이전에는 전역 객체(this)에 접근하는 표준화된 방식이 없었다. 즉, 브라우저에서는 window, Node 환경에서는 global, 웹 워커의 경우 self를 사용해서 전역 객체를 참조했다.

이 때문에 런타임에서 현재 환경을 수동으로 감지하여 전역 객체에 접근하는 적절한 방법을 사용해야 했다.
ES2020부터는 어떤 환경에서든 항상 전역 객체를 참조하는 globalThis를 사용할 수 있다. 브라우저에서는 전역 객체에 직접 접근할 수 없기 떄문에 globalThis가 전역 객체의 프록시를 참조하게 된다.

새로운 globalThis를 사용하면 애플리케이션이 실행되는 환경에 따라 전역 객체에 접근하는 방식이 다른 것에 대해 더 이상 걱정할 필요가 없다.

ES2021의 새로운 기능

1. String.prototype.replaceAll()

replace()는 문자열의 패턴을 다른 것으로 바꿀 수 있는 유용한 메서드이다. 하지만 정규식(RegEx) 패턴이 아닌 단순 문자열 패턴을 사용할 때는 일치하는 첫 번째 항목만 교체 가능하다. 다음 예를 보자

const str = 'I like my dog, my dog loves me';
const newStr = str.replace('dog','cat');
newStr;

// 'I like my cat, my dog loves me"

replaceAll()은 이름에서 알 수 있듯이 문자열 패턴을 대체할 때 일치하는 모든 패턴을 찾아내서 교체한다. 즉 RegEx를 사용하지 않고도 하위 문자열의 모든 패턴을 쉽게 교체할 수 있다.

const str = 'I like my dog, my dog loves me';
const newStr = str.replaceAll('dog','cat');
newStr;

// 'I like my cat, my cat loves me"

2. Promise.any()

최근 몇년동안 프로미스와 관련해서 Promise.all(), ES6의 Promise.race(), ES2020의 Promise.allSettled()와 같은 새로운 메서드가 등장했다. ES2021에서는 Promise.any()라는 새로운 메서드가 도입된다.

사실 이미 메서드 이름만으로 그 기능을 예상할 수 있을 것이다.

Promise.any()는 주어진 프로미스 중 하나라도 성공하면 실행이 완료되지만 그렇지 않다면 모든 프로미스가 실패할 때까지 계속된다.

주의할 점은 Promise.race()와 혼동하면 안 된다는 것이다. Promise.race()에서는 주어진 프로미스 중 하나라도 성공하거나 실패하면 전체 프로미스의 실행이 완료된다.

프로미스가 성공했을 때의 동작은 두 메서드가 비슷하지만, 실패했을 때의 동작은 매우 다르다.

Promise.any() 내부의 모든 프로미스가 실패하면, 모든 프로미스의 실패 이유가 포함된 AggregateError가 발생한다.

실제 구현 예는 다음과 같다.

Promise.any(promises).then(
  (first) => {
    //프로미스 중 하나라도 완료된 경우
  },
  (error) => {
  	//모든 프로미스가 실패한 경우
  }
);

3. 논리연산자와 할당 표현식

ES2021에서는 루비 언어처럼 논리연산자(&&, ||, ??)와 할당 표현식(=)을 결합할 수 있다. 이를 통해 다음과 같은 코드 구현이 가능해졌다.

a ||= b;
// a = a || b와 동일

c &&= d;
// c = c && d와 동일

e ??= f;
// e = e ?? f와 동일

4. 숫자 구분 기호

ES2021에는 숫자 구분 기호가 도입되었으며, 그 덕분에 큰 숫자의 자릿수 그룹을 구분하는 데에 _(밑줄) 문자를 사용하여 숫자를 더 쉽게 읽을 수 있게 되었다.

x = 100_000;
// 100,000과 동일

fraction = 0.000_1;
// 1/10000

5. 약한 참조

MDN에 따르면, 객체에 대한 약한참조란 가비지 콜렉터에서 객체를 회수하는 것을 방지하지 않는 참조이다.

ES2021의 새로운 제안을 통해 WeakRef 클래스를 사용하여 객체에 대한 약한 참조를 만들 수 있게 되었다. 자세한 내용은 다음 자바스크립트 명세서를 참고하자.

https://github.com/tc39/proposal-weakrefs

6. Intl.ListFormat

Intl.ListFormat 객체는 각종 언어별로 목록 서식을 활성화하는 객체의 생성자이다.

예제를 직접 보는 것이 설명보다 쉬울 것이다.

const list = ['Apple', 'Orange', 'Banana'];

new Intl.ListFormat('en.GB, {style: 'long', type: 'conjunction}).format(list);
// Apple, Orange and Banana

new Intl.ListFormat('en.GB, {style: 'long', type: 'disjunction}).format(list);
// Apple, Orange or Banana
profile
The Wandering Caretaker

4개의 댓글

comment-user-thumbnail
2021년 7월 19일

ㄷㄷ

답글 달기
comment-user-thumbnail
2021년 7월 24일

너무 유익한 글이네요

답글 달기
comment-user-thumbnail
2021년 7월 25일

오 유용한 기능들이 몇 개 보이네요! 유익한 글 감사합니다!

1개의 답글