본 글은 Alberto Montalesi의 Complete Guide to Modern JavaScript 를 참고한 포스팅입니다. 오타, 피드백 등은 답글을 달아주세요!
BigInt에 대한 지원은 자바스크립트에서 매우 큰 정수를 저장할 수 있음을 의미한다. 현재 정수의 최댓값은
이며, 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)
을 사용하여 BigInt
를 int
로 변경해야 한다,. 이 예시 에서는 이 번거로움을 피하기 위해 1을 더하는 대신 1n
을 더했다.
ES2020부터는 필요할 때 모듈을 동적으로 가져올 수 있다. 다음 예를 살펴보자.
if(condition1 && condition2) {
const module = await import('./path/to/module.js');
module.doSomething();
}
이 코드처럼 런타임에서 모듈이 필요한지 여부를 판단해서, 필요한 경우에만 async/await
을 사용하여 해당 모듈을 가져오는 게 가능해졌다.
사용자를 나타내는 간단한 객체를 예로 살펴보자
const user1 = {
name: 'Alberto',
age: 27,
work: {
title: 'software developer',
location: 'Vietnam',
},
};
const user2 = {
name: 'Tom',
age: 27,
}
이런 예에서 사용자의 직함에 접근하고 싶다고 가정해보자. 예제에서 work
는 user
객체의 선택적 속성이므로 예전에는 다음과 같이 작성해야 했다.
let jobTitle;
if(user.work) {
jobTitle = user.work.title;
}
혹은 다음과 같이 삼항연산자를 사용하는 방법도 있다.
const jobTitle = user.work ? user.work.title : '';
즉 work
의 title
속성에 접근하기 전에 user
객체가 실제로 work
속성을 가지고 있는지 확인해야 했다는 뜻이다.
단순한 객체를 다룰 때는 그렇게 큰 문제가 아니지만, 접근하려는 속성이 깊게 중첩되어 있는 객체의 경우에는 코드가 많이 복잡해진다.
이때 옵셔널 체이닝 연산자인 ?.
을 사용하면 간결하게 코드를 작성할 수 있다. 옵셔널 체이닝을 이용하여 앞의 코드를 다시 작성해보자.
const jobTitle = user.work?.title
훨씬 간결하고 읽기 좋아졌다.
코드를 보면 user
객체가 work
속성의 존재 여부를 묻는 것처럼 읽히고, 존재한다면 title
속성에 자연스럽게 접근할 수 있다.
const user1JobTitle = user1.work?.title;
// software developer
const user2JobTitle = user2.work?.title;
// undefined
객체에서 사용할 수 없는 속성에 도달하면 연산자는 undefined
를 반환한다.
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},
//]
거짓값과 null
계열의값(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
병합 연산자(??)가 유용하다.
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이 아니라 거짓이므로 연산자가 오른쪽 값을 반환하지 않았다.
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 범위의 문자에 대해 매칭을 진행했고 두 개의 결과를 얻었다.
export
문법ES2020 이전에도 다음과 같이 import
를 할 수는 있었다.
import * as stuff from './test.mjs';
ES2020부터는 export
시에도 동일하게 할 수 있다.
export * as stuff from './test.mjs';
이 코드는 다음 코드와 동일한 역할을 수행한다.
export { stuff };
대단한 기능은 아니지만 import
와 export
문법을 대칭적으로 사용할 수 있게 됐다는 점에 의미가 있다.
import.meta
import.meta 객체는 URL 등 모듈에 대한 정보를 노출한다.
<script type="module" src="test.js"></script>
console.log(import.meta); // {url: "file://home/user/test.js"}
객체에 포함된 URL은 인라인 스크립트의 문서 주소일 수도 있고 외부 스크립트를 가져온 URL일 수도 있다.
globalThis
자바스크립트에서는 ES2020 이전에는 전역 객체(this
)에 접근하는 표준화된 방식이 없었다. 즉, 브라우저에서는 window
, Node 환경에서는 global
, 웹 워커의 경우 self
를 사용해서 전역 객체를 참조했다.
이 때문에 런타임에서 현재 환경을 수동으로 감지하여 전역 객체에 접근하는 적절한 방법을 사용해야 했다.
ES2020부터는 어떤 환경에서든 항상 전역 객체를 참조하는 globalThis
를 사용할 수 있다. 브라우저에서는 전역 객체에 직접 접근할 수 없기 떄문에 globalThis
가 전역 객체의 프록시를 참조하게 된다.
새로운 globalThis
를 사용하면 애플리케이션이 실행되는 환경에 따라 전역 객체에 접근하는 방식이 다른 것에 대해 더 이상 걱정할 필요가 없다.
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"
최근 몇년동안 프로미스와 관련해서 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) => {
//모든 프로미스가 실패한 경우
}
);
ES2021에서는 루비 언어처럼 논리연산자(&&, ||, ??)와 할당 표현식(=)을 결합할 수 있다. 이를 통해 다음과 같은 코드 구현이 가능해졌다.
a ||= b;
// a = a || b와 동일
c &&= d;
// c = c && d와 동일
e ??= f;
// e = e ?? f와 동일
ES2021에는 숫자 구분 기호가 도입되었으며, 그 덕분에 큰 숫자의 자릿수 그룹을 구분하는 데에 _(밑줄) 문자를 사용하여 숫자를 더 쉽게 읽을 수 있게 되었다.
x = 100_000;
// 100,000과 동일
fraction = 0.000_1;
// 1/10000
MDN에 따르면, 객체에 대한 약한참조란 가비지 콜렉터에서 객체를 회수하는 것을 방지하지 않는 참조이다.
ES2021의 새로운 제안을 통해 WeakRef 클래스를 사용하여 객체에 대한 약한 참조를 만들 수 있게 되었다. 자세한 내용은 다음 자바스크립트 명세서를 참고하자.
https://github.com/tc39/proposal-weakrefs
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
ㄷㄷ