Beomy

[JavaScript] ECMAScript 2020

  12 mins read  

ES2020(ES11)에 추가된 기능들을 살펴보도록 하겠습니다.

String.prototype.matchAll

문자열에서 일치하는 정규식을 iterator 형태로 반환하는 함수입니다. 사용방법은 match와 동일합니다.

for of 사용

iterator을 반환하기 때문에 for of 문을 사용할 수 있습니다.

const str = '11a22ba';
const result = str.matchAll(/a/g);
for (const item of result) {
  console.log(item)
}
/*
['a', index: 2, input: '11a22ba', groups: undefined]
['a', index: 6, input: '11a22ba', groups: undefined]
*/

문자열 사용

matchAll 함수의 파라미터로 문자열을 전달할 수도 있습니다.

const str = '11a22ba';
const result = str.matchAll('a');
result.next(); // { value: ['a', index: 2, input: '11a22ba', groups: undefinded], done: false }
result.next(); // { value: ['a', index: 6, input: '11a22ba', groups: undefinded], done: false }
result.next(); // { value: undefined, done: true }

주의사항

matchAll 함수의 파라미터로 정규식을 전달할 경우 글로벌 옵션(g)을 줘야 합니다. 글로벌 옵션이 없는 경우 non-global RegExp 에러가 발생합니다.

const str = '11a22ba';
const result = str.matchAll(/a/); // Uncaught TypeError: String.prototype.matchAll called with a non-global RegExp argument

import()

동적으로 파일을 가져올 수 있게 하는 함수입니다. Promise를 반환합니다.

// say.js
export function hi() {
  alert(`안녕하세요.`);
}

export function bye() {
  alert(`안녕히 가세요.`);
}
let {hi, bye} = await import('./say.js');

hi();
bye();

BigInt

자바스크립트는 2^53 보다 큰 수를 표현할 수 없습니다. 2^53 보다 큰 수를 표현할 수 있도록 BigInt가 추가되었습니다.

const num = Number.MAX_SAFE_INTEGER;
console.log(num); // 9007199254740991
console.log(num + 1); // 9007199254740992
console.log(num + 2); // 9007199254740992

const bigInt = 9007199254740991n; // 또는 BigInt(9007199254740991)
console.log(bigInt); // 9007199254740991
console.log(bigInt + 1n); // 9007199254740992
console.log(bigInt + 2n); // 9007199254740993

typeof 1nbigint이기 때문에 때문에 새로운 타입이 생겼다고 볼 수 있습니다. 타입에 차이가 있기 때문에 1n1은 아래와 같이 차이가 있습니다.

typeof 1n; // bigint
1n === 1 // false
1n == 1 // true

Number와 유사점

BigInt 타입을 사용할 때, Number 타입과 유사한 방법으로 사용할 수 있는 연산들을 살펴보겠습니다.

크기 비교

1n !== 1이지만 NumberBigInt 사이의 크기 비교가 가능합니다.

1n < 2 // true
2n > 1 // true
2 > 2 // false
2n > 2 // false
2n >= 2 // true

정렬

정렬도 Number와 유사합니다.

const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
mixed.sort(); // [-12n, 0, 0n, 10, 4n, 4, 6]

조건 판단

조건 판단 기준 역시 Number와 비슷합니다.

if (0n) {
  console.log('Hello from the if!');
} else {
  console.log('Hello from the else!');
} // "Hello from the else!"

0n || 12n // 12n
0n && 12n // 0n
Boolean(0n) // false
Boolean(12n) // true
!12n // false
!0n // true

주의사항

Number 타입과 동일하게 BigInt 타입을 사용할 때 주의해야 할 사항을 살펴보겠습니다.

BigIntNumber 간의 계산

BigIntNumber간의 계산은 TypeError가 발생합니다.

1n + 2 // TypeError: Cannot mix BigInt and other types, use explicit conversions
1n * 2 // TypeError: Cannot mix BigInt and other types, use explicit conversions

BigInt에서 Number로 형 변환

+ 연산을 사용해서 BigIntNumber로 형 변환도 불가능합니다. BigIntNumber로 형 변환을 하기 위해서는 Number 함수를 사용해야 합니다.

+1n // TypeError: Cannot convert a BigInt value to a number
Number(1n) // 1

Math 함수와 | 연산

Math 함수, Number| 연산을 사용할 때 TypeError가 발생합니다.

Math.round(1n) // TypeError: Cannot convert a BigInt value to a number
Math.max(1n, 10n) // TypeError: Cannot convert a BigInt value to a number
1n|0 // TypeError: Cannot mix BigInt and other types, use explicit conversions

Promise.allSettled

아래 표와 같이 Promise.allSettled는 Promise 작업이 모두 종료되었을 때에 마치는 함수입니다.

namedescription
Promise.allSettled모든 작업이 종료되면 종료됨
Promise.all모든 작업이 resolve되거나, reject 된 작업이 발생되면 종료됨
Promise.racereject 혹은 resolve 된 작업이 발생되면 종료됨
Promise.anyresolve 된 작업이 발생되면 종료됨

사용 방법과 응답 결과는 아래 코드와 같습니다.

function promise1 () {
  return new Promise((res, rej) => {
    rej('Reject');
  });
}

function promise2 () {
  return new Promise((res, rej) => {
    res('Resolve');
  });
}

async function main () {
  const list = [promise1(), promise2()];
  const result = await Promise.allSettled(list);
  console.log(result);
}

main() // [{status: 'rejected', reason: 'Reject'}, {status: 'fulfilled', value: 'Resolve'}]

globalThis

브라우저에서 전역 객체는 window이고, Node.js의 전역 객체는 global입니다. 런타임마다 전역 객체가 달라서, 아래와 같이 전역 객체를 가져오는 공통 코드를 만들어야 합니다.

var getGlobal = function () {
	// the only reliable means to get the global object is
	// `Function('return this')()`
	// However, this causes CSP violations in Chrome apps.
	if (typeof self !== 'undefined') { return self; }
	if (typeof window !== 'undefined') { return window; }
	if (typeof global !== 'undefined') { return global; }
	throw new Error('unable to locate global object');
};

ES2020에서 globalThis가 등장해서 아래 코드와 같이 어떤 런타임이든 globalThis로 전역 객체를 가져올 수 있습니다.

// browser
globalThis === window; // true
// Node.js
globalThis === global; // true

for-in 순서

기존에는 브라우저마다 for-in 문의 반복 순서를 보장하지 못했습니다(브라우저마다 반복 순서가 다를 수 있었습니다). ES2020에서 for-in 순서를 보장되도록 스펙이 수정되었습니다.

Optional Chaining

ES2020에 추가된 사양 중에 개인적으로 가장 유용하게 사용하는 기능입니다. 객체의 값을 안전하게 가져오기 위해서는 아래와 같이 작성되어야 합니다.

var street = user.address && user.address.street;

메서드를 호출 후 응답 결과가 nullundefined일 경우 아래와 같은 예외 처리가 필요합니다,

var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined

Optional Chaining을 사용하면 위의 코드들이 아래와 같이 정리됩니다.

var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value

아래와 같이 함수도 Optional Chaining으로 호출할 수 있습니다.

iterator.return?.()

주의사항

Optional Chaining이 지원하지 않는 문법을 살펴보도록 하겠습니다.

  • optional construction: new a?.()
  • optional template literal: a?.`string`
  • Optional Chaining 후에 오는 constructor와 template literal: new a?.b(), a?.b`string`
  • optional property assignment: a?.b = c
  • optional super: super?.(), super?.foo
  • 그 외의 예약어: new?.target, import?.('foo')

Nullish coalescing Operator

ES2020에 ?? 연산자가 추가되었습니다. ||와 비교해서 살펴보도록 하겠습니다. undefined, null, '', 0, false 모두 falsy 한 값이기 때문에 || 연산시 false로 뒤에 정의한 값이 할당됩니다.

const response = {
  settings: {
    nullValue: null,
    height: 400,
    animationDuration: 0,
    headerText: '',
    showSplashScreen: false
  }
};

const undefinedValue = response.settings.undefinedValue || 'some other default'; // some other default
const nullValue = response.settings.nullValue || 'some other default'; // some other default
const headerText = response.settings.headerText || 'Hello, world!'; // Hello, world!
const animationDuration = response.settings.animationDuration || 300; // 300
const showSplashScreen = response.settings.showSplashScreen || true; // true

?? 연산은 아래 코드와 같습니다.

const response = {
  settings: {
    nullValue: null,
    height: 400,
    animationDuration: 0,
    headerText: '',
    showSplashScreen: false
  }
};

const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // some other default
const nullValue = response.settings.nullValue ?? 'some other default'; // some other default
const headerText = response.settings.headerText ?? 'Hello, world!'; // ''
const animationDuration = response.settings.animationDuration ?? 300; // 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // false

?? 연산의 경우 undefindednull인 경우에만 falsy로 처리됩니다.

참고