2009년에 ES5가 등장하고 2015년에 ES6가 등장하며 큰 변화를 겪은 이후, ECMA는 매년 새로운 자바스크립트 표준을 발표합니다. ES6 이후 추가된 내용중 일부는 이미 사용하고 있지만, 자세히 알아본 적이 없는 것 같아서 정리하는 시간을 가져봤습니다. ECMAScript에 대한 자세한 설명은 생략하고, ES6(ES2015) 이후에 어떤 것들이 추가되었는지 나열해 보려고 합니다.

ES2016

ES2016에서는 두 가지 기능이 새롭게 도입되었습니다.

  • Array.prototype.includes()
  • 지수 연산자(**)

ES2016-1. Array.prototype.includes()

includes() 메서드는 배열에 특정 원소가 포함되어 있으면 true를 반환하고 그렇지 않으면 false를 반환합니다.

const numbers = [1, 2, 4, 8];
numbers.includes(2);
//true
numbers.includes(3);
//false

includes()에 두 번째 값으로 인덱스를 추가해서 원소를 검색할 수 있습니다. 기본값은 0이지만 음수를 전달할 수도 있습니다.
MDN - includes

ES2016-2. 지수 연산자

//ES2016 이전의 지수 계산
Math.pow(2, 2); // 4
Math.pow(2, 3); // 8

// 지수 연산자 **를 사용
2**2; // 4
2**3; // 8

ES2017

ES2017에서는 익히 잘 알고 계시는 async, await 등 많은 기능이 추가되었습니다. async를 제외한 나머지 기능을 알아보겠습니다.

ES2017-1. 문자열 패딩

문자열 끝 부분이나 시작 부분을 다른 문자열로 채워 주어진 길이를 만족하는 새로운 문자열을 만들어낼 수 있습니다. String.prototype.padStart(), String.prototype.padEnd()로 사용할 수 있습니다.

"hello".padStart(6); " hello"
"hello".padEnd(6); "hello "
"hello".padStart(3); "hello" // 문자열 길이보다 목표 문자열 길이가 짧다면 채워넣지 않고 그대로 반환

// 사용자가 지정한 값으로 채우는 것도 가능합니다.
"hello".padEnd(20, "*");
// "hello***************" 

MDN - padStart
MDN - padEnd

ES2017-2. Object.entries()와 Object.values()

객체 내부의 값에 접근하는 방법입니다.

const ppap = {
  pplead: 'Ten Kim',
  aplead: 'GJ Han',
  pp: ['Widget Lee'],
  ap: ['Someone Min']
}

Object.entries(ppap); // 객체의 키와 값을 포함하는 배열의 배열을 반환
[
  ["pplead", "Tem Kim"],
  ["aplead", "GJ Han"],
  ["pp", ["Widget Lee"]],
  ["ap", ["Someone Min"]]
]

Object.values(ppap); // 객체의 값이 담긴 배열을 반환
[
  "Ten Kim",
  "GJ Han",
  [ "Widget Lee" ],
  [ "Someone Min" ]
]

// 이런 식으로 객체를 map으로 쉽게 바꿀 수 있음
const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }

MDN - Object.entries
MDN - Object.values

ES2017-3. Object.getOwnPropertyDescriptors()

객체가 소유한 모든 상속받지 않은 속성 설명자를 반환합니다.

const myObj = {
  name: "Woongki",
  age: '30 over',
  get myname() {
    return this.name;
  }
}

Object.getOwnPropertyDescriptors(myObj);

{
  "name": {
    "value": "Woongki",
	"writable": true,
	"enumerable": true,
	"configurable": true
  },
  "age": {
	"value": "30 over",
	"writable": true,
	"enumerable": true,
	"configurable": true
  },
  "myname": {
	"enumerable": true,
	"configurable": true,
	"set": undefined,
	"get": myname() {
	  return this.name;
	}
  }
}

MDN - Object.getOwnPropertyDescriptors

ES2017-4. Trailing Comma

배열 리터럴에서는 항상 trailing comma가 허용되었습니다. ES6에서 객체 리터럴에도 도입되었고, 함수에도 도입된 것이 ES2017입니다.

function f(p) {}
function f(p,) {}

(p) => {};
(p,) => {};

MDN - Trailing commas

ES2017-5. Atomics, SharedArrayBuffer

  • 멀티 스레드 환경을 지원하기 위한 기능들입니다. 이 부분은 저도 좀 더 심도있게 공부한 후에 더 자세히 서술할 수 있을 것 같아서 일단 간략히 설명하고 지나가겠습니다.
  • SharedArrayBuffer는 고정된 길이의 바이너리 데이터 버퍼를 표현하는데 사용되며 컨텐츠는 0으로 초기화됩니다. 메인스레드 혹은 다른 worker와의 메모리 공유를 위해 쓰일 수 있습니다.
  • 메모리가 공유되면 여러 스레드가 메모리에서 동일한 데이터를 읽고 쓸 수 있습니다. Atomics를 이용한 작업은 이러한 환경에서도 정확하게 값을 읽고 쓸 수 있게 해줍니다. 또 Atomics를 이용한 작업은 다음 작업이 시작되기 전에 완료되고, 중단되지 않는 것이 보장됩니다.
  • 허나 SharedArrayBuffer는 Spectre 에 대한 대응으로 2018년 1월 이후 거의 대부분의 브라우저에서 비활성화가 되어있다가 크롬68, 엣지79, 파이어폭스79 이후 버전의 브라우저에서만 사용이 가능합니다.
    MDN - SharedArrayBuffer
    MDN - Atomics

ES2018

ES2018-1. Object rest/spread

ES6에서 배열의 spread, rest가 가능해졌고, ES2018에서는 객체에도 사용하는 것이 가능해졌습니다. 배열과 마찬가지로 객체도 쉽게 얕은 복사를 할 수 있게 되었습니다.

const myObj = {
  a: 1,
  b: 3,
  c: 'cc',
  d: 100
};

const {a, b, ...z} = myObj;
console.log(a); // 1
console.log(b); // 3
console.log(z); // { "c": "cc", "d": 100 }


const spread = {
  ...myObj,
  a: 10,
  e: 30,
};

console.log(spread);
/**
{
  "a": 10,
  "b": 3,
  "c": "cc",
  "d": 100,
  "e": 30
}
*/

ES2018-2. Async iteration

제너레이터 함수와 for of 문에서 async를 사용할 수 있습니다.

(async() => {
  const promises = [1, 2, 3].map((timer) => (
    new Promise((resolve) => {
      setTimeout(() => resolve(timer), timer*1000);
    })
  ));

  for await (const result of promises) {
    console.log(result);
  }
})();

MDN-for…of

ES2018-3. Promise.prototype.finally()

finally() 메소드는 Promise가 처리되면 충족되거나 거부되는지의 여부에는 상관없이 무조건 콜백 함수가 실행됩니다. finally() 또한 프로미스를 반환하고, then과 catch로 계속 연결할 수 있지만, finally가 반환한 값이 아니라 그 전의 프로미스가 반환한 값을 갖게 됩니다. 프로미스의 성공 여부와 관계가 없기 때문에 finally는 아무런 인자도 받지 않습니다.

const myPromise = new Promise((resolve, reject) => {
  resolve();
});

myPromise.then(() =>{
  console.log('1');
  return 'still working at 2am'
}).finally(res => { // 아무런 인자도 받지 않기 때문에 undefined가 나옴
  console.log(2, res)
  return 'finally'
}).then(res => {
  console.log(3, res);
});

// 1
// 2 undefined 
// 3 still working at 2am

ES2018-4. 정규식 기능 추가

ES2018에는 네 가지 새로운 정규식 관련 기능이 추가되었습니다.

  • s(dotAll) 플래그 -> . 표현식은 개행문자를 제외한 모든 문자였으나, s플래그를 달면 개행식도 포함하게 됩니다.
    /foo.bar/s.test('foo\nbar'); // true
    
  • 명명된 캡쳐 그룹 -> (?<name>) 구문을 사용하여 캡쳐 그룹에 원하는 이름을 붙일 수 있습니다.
    let regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
    let result = regex.exec('2021-06-21');
    console.log(result.groups);
    {
        "year": "2021",
        "month": "06",
        "day": "21"
    }
    
  • 룩비하인드 어서션
    • 룩비하인드 어서션을 사용하면 패턴 앞에 다른 패턴이 있는지 여부를 확인할 수 있다고 합니다.
    • 긍정 룩비하인드 어서션
      const regex = /(?<=\$)\d+(\.\d*)?/;
      const result = regex.exec('$10.53');
      [
      "10.53",
      ".53"
      ]
      const result2 = regex.exec('&10.53');
      // null
      
    • 부정 룩비하인드 어서션 (?<!…)
  • 유니코드 속성 이스케이프 \p{...}(긍정), \P{...}(부정) 형식으로 유니코드 속성 이스케이프를 사용할 수 있습니다.
    const sentence = 'A ticket to 大阪 costs ¥2000 👌.';
    
    const regexpEmojiPresentation = /\p{Emoji_Presentation}/gu;
    console.log(sentence.match(regexpEmojiPresentation));
    // expected output: Array ["👌"]
    
    const regexpNonLatin = /\P{Script_Extensions=Latin}+/gu;
    console.log(sentence.match(regexpNonLatin));
    // expected output: Array [" ", " ", " 大阪 ", " ¥2000 👌."]
    
    const regexpCurrencyOrPunctuation = /\p{Sc}|\p{P}/gu;
    console.log(sentence.match(regexpCurrencyOrPunctuation));
    // expected output: Array ["¥", "."]
    

    MDN - Unicode property escapes

ES2019

ES2019-1. Array.prototype.flat()과 Array.prototype.flatMap()

  • Array.prototype.flat()
    Array.prototype.flat()은 지정한 깊이까지 배열을 재귀적으로 평면화합니다. 깊이 인수가 지정되지 않으면 1이 기본값이고, Infinity로 지정하면 모든 중첩 배열을 평면화할 수 있습니다.

    const arr1 = [1, 2, [3, 4]];
    arr1.flat();
    // [1, 2, 3, 4]
      
    const arr2 = [1, 2, [3, 4, [5, 6]]];
    arr2.flat();
    // [1, 2, 3, 4, [5, 6]]
      
    const arr3 = [1, 2, [3, 4, [5, 6]]];
    arr3.flat(2);
    // [1, 2, 3, 4, 5, 6]
      
    const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
    arr4.flat(Infinity);
    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      
    // 배열의 빈 부분도 제거합니다
    const arr5 = [1, 2, , 4, 5];
    arr5.flat();
    // [1, 2, 4, 5]
    

    MDN - flat Node11부터 사용 가능하고 IE는 불가합니다. (참고로 오늘 소개한 모든 기능이 IE에선 불가합니다..)

  • Array.prototype.flatMap() 메서드는 먼저 매핑함수를 사용해 각 엘리먼트에 대해 map 수행 후, 결과를 새로운 배열로 평탄화합니다. 이는 깊이 1의 flat이 뒤따르는 map과 동일하지만, flatMap 은 아주 유용하며 둘을 하나의 메소드로 병합할 때 조금 더 효율적입니다.

    let arr1 = ["it's Sunny in", "", "California"];
    
    arr1.map(x=>x.split(" "));
    // [["it's","Sunny","in"],[""],["California"]]
    
    arr1.flatMap(x => x.split(" "));
    // ["it's","Sunny","in", "", "California"]
    

    MDN - flatMap

ES2019-2. Object.fromEntries()

Object.fromEntries는 키/값 쌍이 포함된 iterable을 객체로 변환합니다.(Map도 객체로 변환이 가능). Node12 이상 버전부터 가능합니다.

const keyVal = [
  ['key1', 'val1'],
  ['key2', 'val2']
];
const obj = Object.fromEntries(keyVal);
console.log(obj);
{
  "key1": "val1",
  "key2": "val2"
}

MDN - fromEntries

ES2019-3. String.prototype.trimStart()와 String.prototype.trimEnd()

trimStart는 문자열 시작 부분의 공백을 제거하고 trimEnd는 문자열 끝 부분의 공백을 제거합니다.

const greeting = '   Hello world!   ';

console.log(greeting);
// expected output: "   Hello world!   ";

console.log(greeting.trimStart());
// expected output: "Hello world!   ";

console.log(greeting.trimLeft());
// expected output: "Hello world!   ";

console.log(greeting.trimEnd());
// expected output: "   Hello world!";

console.log(greeting.trimRight());
// expected output: "   Hello world!";

MDN - trimStart

ES2019-4. 선택적 catch 할당

ES2019 이전에는 catch절에 항상 예외 변수를 포함해야 하지만, ES2019에서는 이를 생략할 수 있습니다.

try {
  ...
} catch {
  ...
}

ES2019-5. Function.prototype.toString()

함수 객체의 .toString() 메서드는 함수의 소스 코드를 나타내는 문자열을 반환합니다. ES2016까지는 소스 코드에서 주석이나 공백 문자를 제거했지만, ES2019에서 개정되어 문자열에 주석 등도 포함됩니다.

function sum(a, b) {
  return a+b;
  // 두 인자의 합을 리턴합니다.
}

console.log(sum.toString());
'function sum(a, b) { return a+b; // 두 인자의 합을 리턴합니다. }'

MDN - toString

ES2019-6. Symbol.prototype.description

심벌 객체의 description은 해당 심벌 객체의 설명을 반환합니다.

Symbol('desc').toString();   // "Symbol(desc)"
Symbol('desc').description;  // "desc"

MDN - description

ES2020

ES2020-1. BigInt

현재 정수의 최대값은 2**53-1이며 Number.MAX_SAFE_INTEGER를 통해 확인할 수 있습니다. BigInt는 정수 리터럴의 뒤에 n을 붙이거나(10n) 함수 BigInt()를 호출해 생성할 수 있습니다.

BigIntNumber는 어떤 면에서 비슷하지만 중요한 차이점이 있습니다. 예컨대 BigInt는 내장 Math객체의 메서드와 함께 사용할 수 없고, 연산에서 Number와 혼합해 사용할 수 없습니다. 따라서 먼저 같은 자료형으로 변환해야 합니다. 그러나, BigIntNumber로 바뀌면 정확성을 잃을 수 있으니 주의해야 합니다. 또한 bigDecimal이 아니기 때문에 소수점 이하는 언제나 버립니다.

const theBiggestInt = 9007199254740991n;

const bigintSum = theBiggestInt + 1n;
// 9007199254740992n

const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n

typeof bigintSum
// "bigint"

MDN - BigInt

ES2020-2. Dynamic import

ES2020부터는 필요할 때 모듈을 동적으로 가져올 수 있습니다.

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

MDN - import

ES2020-3. 옵셔널 체이닝

연산자 ?. 는 체인의 각 참조가 유효한지 명시적으로 검증하지 않고, 연결된 객체 체인 내에 깊숙이 위치한 속성 값을 읽을 수 있습니다. ?. 연산자는 . 체이닝 연산자와 유사하게 작동하지만, 만약 참조가 nullish(null 또는 undefined)이라면, 에러가 발생하는 것 대신에 표현식의 리턴 값은 undefined로 단락됩니다.

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const catName = adventurer.cat?.name;
console.log(catName);
// 'Dinah'

const dogName = adventurer.dog?.name;
console.log(dogName);
// undefined


MDN - optional chaining (Node v14부터 사용 가능)

ES2020-4. Promise.allSettled()

allSettled는 성공 실패 여부와 무관하게 모든 프로미스들이 완료될 떄까지 기다렸다가 각각의 결과를 설명하는 객체 배열을 반환합니다.

const promiseArr = [
  new Promise((resolve, reject) => setTimeout(resolve, 1000, 'abc')),
  new Promise((resolve, reject) => setTimeout(reject, 2000)),
  new Promise((resolve, reject) => setTimeout(resolve, 3000)),
];

Promise.allSettled(promiseArr).then(data => console.log(data));

[
  {
    "status": "fulfilled",
    "value": "abc"
  },
  {
    "status": "rejected"
  },
  {
    "status": "fulfilled"
  }
]

MDN - allSettled (Node v12.9 이상)

ES2020-5. Null coalescing operator(null 병합 연산자)

null 병합 연산자는 왼쪽 피연산자가 null 또는 undefined일 때 오른쪽 연산자를 반환하고 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자입니다. 이는 왼쪽 피연산자가 falsy 값에 해당할 경우 오른쪽 피연산자를 반환하는 or(||)와는 대조됩니다.

const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// expected output: 0

MDN - Nullish coalescing operator (Node v14 이상)

ES2020-6. String.prototype.matchAll()

matchAll 메서드는 지정된 정규식에 대해 문자열과 일치하는 모든 결과의 iterator를 반환하는 메서드입니다.(캡쳐링 그룹 포함) 정규 표현식 뒤에 g flag를 사용해주어야 합니다.

const regEx = /[a-d]/g;
const str = 'Lorem ipsum dolor sit amet';
const result = str.matchAll(regEx);

console.log(result.next()); 
{
  "value": ["d"],
  "done": false
}
console.log(result.next());
{
  "value": ["a"],
  "done": false
}
console.log(result.next());
{
  "done": true
}

MDN - matchAll

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

이제

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

// 아래의 코드와 동일한 역할을 수행한다
export { stuff };

같이 선언할 수 있습니다… 의미는 잘 모르겠..

ES2020-8. import.meta

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

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

아래와 같은 응용이 가능!

<script type="module">
import './index.mjs?someURLInfo=5';
</script>
// index.mjs

new URL(import.meta.url).searchParams.get('someURLInfo'); // 5

MDN - import.meta

ES2020-8. globalThis

ES2020 전에는 전역 객체에 접근하는 표준화된 방식이 없었습니다. 브라우저에서는 window, node에서는 global, 웹 워커의 경우 self 등을 사용해 전역 객체에 접근했었습니다. ES2020부터는 어떤 환경에서든 항상 전역 객체를 참조하는 globalThis를 사용할 수 있습니다.(Node v12부터)

ES2021

아직 스펙이 정식으로 결정된 것은 아니지만 많은 제안 중에 4단계에 도달하여 다음 스펙에 포함될 예정인 것들을 몇 개 알아봅시다.

ES2021-1. String.prototype.replaceAll()

기존의 replace() 메서드는 문자열의 패턴을 다른 것으로 바꿀 수 있는 유용한 메서드였지만, 정규식을 쓰지 않으면 일치하는 첫 번째 항목만 교체가 가능했습니다. 그러나 replaceAll() 단순 문자열 패턴을 대체할 때도 일치하는 모든 문자열을 교체합니다.

const str = 'I like my dog, my dog is very cute.';
const newStr = str.replaceAll('dog', 'cat');

console.log(newStr);
//I like my cat, my cat is very cute.

MDN - replaceAll (chrome85, node v15 이상)

ES2021-2. Promise.any()

Promise.any()는 주어진 promise 중 하나라도 성공하면 실행을 완료하지만, 그렇지 않다면 모든 promise가 실패할 때 까지 계속됩니다. Promise.race()는 하나라도 성공 혹은 실패할 시에 종료되는 것과 차이가 있습니다.

const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 500, 'slow'));

const promise4 = new Promise((resolve, reject) => setTimeout(reject, 100, 'quick'));
const promise5 = new Promise((resolve, reject) => setTimeout(reject, 500, 'slow'));

const promises1 = [promise1, promise2, promise3]
const promises2 = [promise1, promise4, promise5];

Promise.any(promises1).then((value) => console.log(value)).catch(err => console.log(err));
// 'quick'


Promise.any(promises2).then((value) => console.log(value)).catch(err => console.log(err));
// AggregateError: All promises were rejected

MDN - Promise.any() (chrome85, Node v15 이상)

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

ES2021부터는 논리 연산자와 할당 표현식을 결합할 수 있습니다.

const a = { duration: 50, title: ''};

a.duration ??= 10;
console.log(a.duration);
// 50

a.speed ??= 25;
console.log(a.speed);
// 25

a.duration &&= 60;
console.log(a.duration);
// 60

a.title ||= 'Logical or Assignment'
console.log(a.title);
// 'Logical or Assignment'

MDN - Logical nullish assignment (chrome85, Node v15 이상)

ES2021-4. 숫자 구분 기호

ES2021에는 숫자 구분 기호가 도입되었으며, 큰 자릿수 숫자를 구분하는데에 언더바를 사용하여 쉽게 표시할 수 있게 되었습니다.

const x = 100_000;
console.log(x)
// 100000;

MDN 문서를 찾지 못하여 proposal 명세서를 남깁니다. TC39 - proposal-numeric-separator

ES2021-5. WeakRef

WeakRef를 통해 특정 객체에 일반 참조가 없으면 약한 참조가 남아있어도 가비지 콜렉터가 해당 객체를 메모리에서 제거한다고 합니다. 이 부분도 조금 더 자세한 공부가 필요할 것 같아서 참조 링크를 남기고, 후에 다시 자세히 설명할 기회를 가지려고 합니다.
MDN - WeakRef

ES2021-6. Intl.ListFormat

각종 언어별로 목록 서식을 활성화하는 객체의 생성자라고 합니다. 직접 보는 것이 이해가 빠를 것 같습니다.

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

console.log(new Intl.ListFormat('ko', {style:'long', type: 'conjunction'}).format(list));
// 'Apple, Orange 및 Banana'
console.log(new Intl.ListFormat('ko', {style:'long', type: 'disjunction'}).format(list));
// 'Apple, Orange 또는 Banana'

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

MDN - ListFormat

ES2021-7. Intl.DateTimeFormat의 dateStyle 및 timeStyle

직접 봅시다!

new Intl.DateTimeFormat("ko", {dateStyle: "short"}).format(Date.now());
// '21. 6. 21.'
new Intl.DateTimeFormat("ko", {dateStyle: "medium"}).format(Date.now());
// '2021. 6. 21.'
new Intl.DateTimeFormat("ko", {dateStyle: "long"}).format(Date.now());
// "2021년 6월 21일"

new Intl.DateTimeFormat("en", {dateStyle: "short"}).format(Date.now());
// '6/21/21'
new Intl.DateTimeFormat("en", {dateStyle: "medium"}).format(Date.now());
// 'Jun 21, 2021'
new Intl.DateTimeFormat("en", {dateStyle: "long"}).format(Date.now());
// "June 21, 2021"
new Intl.DateTimeFormat("en", {timeStyle:"short"}).format(Date.now());
// 1:11 PM
new Intl.DateTimeFormat("en", {timeStyle:"medium"}).format(Date.now());
// 1:11:50 PM
new Intl.DateTimeFormat("en", {timeStyle:"long"}).format(Date.now());
// "1:11:50 PM GMT+9"

new Intl.DateTimeFormat("ko", {timeStyle:"short"}).format(Date.now());
// 오후 1:12
new Intl.DateTimeFormat("ko", {timeStyle:"medium"}).format(Date.now());
// 오후 1:12:24
new Intl.DateTimeFormat("ko", {timeStyle:"long"}).format(Date.now());
// "오후 1시 12분 24초 GMT+9"

마무리하며

지금까지 ES6 이후에 새로이 추가된 스펙들에 대해서 간략하게 알아보았습니다. 이 글을 작성한 시점과 업로드하는 시점은 차이가 좀 있는데요, 그 사이에 ES2021 스펙이 어느 정도 확정이 된 것 같습니다. 여러 proposal에 대한 정보는 github의 tc39에서 볼 수 있으니 관심이 있으시면 보시는 것도 좋을 것 같습니다. 최대한 확인한다고 했지만 예제나 정보에 틀린 내용이 있을 수 있는데요, 언제든지 woongki@dable.io로 제보 주시면 좋겠습니다. 읽어주셔서 감사합니다.

참조(예제 및 설명)