JavaScript의 표준 이름은 ECMAScript입니다. ES5, ES6, ES2020 같은 이름은 이 표준의 버전입니다. 버전을 차례로 훑어보면 기능 목록보다 먼저 눈에 들어오는 것이 있습니다. 표준을 내놓는 방식이 한 번 크게 바뀌었다는 점입니다.
ES1, ES2, ES3# 1997년에 첫 표준 ES1이 나왔고, 1998년에 ES2, 1999년에 ES3가 뒤따랐습니다. ES3에서 정규 표현식과 try/catch가 들어왔습니다.
ES4# ES4는 타입과 모듈을 비롯해 많은 기능을 한꺼번에 넣으려 했습니다. 변화가 너무 컸고, 결국 공개되지 못한 채 취소됐습니다. 여기서 나온 아이디어들은 이후 버전에 나뉘어 들어갑니다.
ES5# 2009년, ES3 이후 10년 만에 나온 표준입니다. 문법을 크게 바꾸는 대신 빠져 있던 것을 채웠습니다.
'use strict': 실수로 전역 변수를 만들거나, 지울 수 없는 속성을 지우려 하거나, 매개변수 이름을 중복해 쓰는 것을 오류로 잡아 줍니다.JSON.parse()와 JSON.stringify(): 그전까지는 JSON을 다루려면 외부 라이브러리가 필요했습니다.배열 메소드: forEach, map, filter, reduce, every, some. Object.defineProperty(): getter와 setter, 열거 여부처럼 속성의 세부 동작을 정할 수 있게 됐습니다.ES6/ES2015# 가장 유명한 버전입니다. 지금 쓰는 JavaScript의 모습은 대부분 여기서 정해졌습니다.
let과 const: 블록 스코프를 가지는 변수 선언입니다.화살표 함수: 문법이 짧고, 자신만의 this를 만들지 않고 바깥 스코프의 this를 씁니다. 클래스: 프로토타입 기반 상속을 더 읽기 쉬운 문법으로 씁니다. Promise: 콜백을 중첩하는 대신 비동기 작업을 체인으로 잇습니다.배열 spread와 나머지 매개변수: Array.concat()이나 arguments 객체 없이 배열을 합치고 인자를 배열로 받습니다. const arr1 = [ 1 , 2 , 3 ];
const arr2 = [ ... arr1, 4 , 5 ]; // [1, 2, 3, 4, 5]
function sum ( ... nums ) {
return nums. reduce (( acc , num ) => acc + num, 0 );
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
const arr1 = [ 1 , 2 , 3 ];
const arr2 = [ ... arr1, 4 , 5 ]; // [1, 2, 3, 4, 5]
function sum ( ... nums ) {
return nums. reduce (( acc , num ) => acc + num, 0 );
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
ES5에서 ES6까지는 6년이 걸렸습니다. ES6부터는 이름에 연도가 붙고, 이후로는 준비된 기능만 모아 매년 작은 버전을 냅니다.
ES7/ES2016# 지수 연산자 **와 Array.prototype.includes가 들어왔습니다. 그전에는 indexOf가 돌려주는 값을 -1과 비교해야 했는데, includes는 true/false로 바로 답합니다.
ES8/ES2017# async/await가 들어왔습니다. Promise의 then 체이닝이 길어지면 읽기 어렵고 에러 처리도 번거로웠는데, 비동기 코드를 동기 코드처럼 위에서 아래로 쓸 수 있게 됐습니다.
ES9/ES2018# 객체에도 spread와 rest를 쓸 수 있게 됐습니다. 그전에는 Object.assign()으로 객체를 복사하고 합쳐야 했습니다.
const base = { a: 1 , b: 2 };
const merged = { ... base, c: 3 }; // { a: 1, b: 2, c: 3 }
const { a , ... rest } = merged; // a = 1, rest = { b: 2, c: 3 }
const base = { a: 1 , b: 2 };
const merged = { ... base, c: 3 }; // { a: 1, b: 2, c: 3 }
const { a , ... rest } = merged; // a = 1, rest = { b: 2, c: 3 }
for await...of와 비동기 제너레이터도 이때 들어와, 비동기로 도착하는 데이터를 반복문으로 다룰 수 있게 됐습니다.
ES10/ES2019# flat과 flatMap은 중첩된 배열을 펼칩니다. flatMap은 map을 한 뒤 한 단계 펼친 것과 같습니다.
const arr = [ 1 , 2 , 3 , 4 , 5 ];
const result = arr. flatMap (( x ) => [x, x * 2 ]); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]
const arr = [ 1 , 2 , 3 , 4 , 5 ];
const result = arr. flatMap (( x ) => [x, x * 2 ]); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]
Object.fromEntries는 키와 값의 쌍으로 된 배열을 객체로 바꿉니다. Object.entries의 반대입니다.
const entries = [[ 'name' , '정민' ], [ 'age' , 20 ]];
const obj = Object. fromEntries (entries); // { name: '정민', age: 20 }
const entries = [[ 'name' , '정민' ], [ 'age' , 20 ]];
const obj = Object. fromEntries (entries); // { name: '정민', age: 20 }
ES11/ES2020# BigInt는 아주 큰 정수를 다루는 새 원시 타입입니다. Number가 안전하게 다룰 수 있는 가장 큰 정수는 2^53 - 1인데, BigInt에는 이 제한이 없습니다.
const bigNum = 9007199254740991 n ; // 접미사 n이 BigInt를 나타낸다
const bigNum = 9007199254740991 n ; // 접미사 n이 BigInt를 나타낸다
Promise.allSettled는 성공이든 실패든 모든 프로미스가 끝날 때까지 기다립니다. Promise.all은 하나라도 실패하면 바로 실패합니다.
const promises = [ Promise . resolve ( 1 ), Promise . reject ( 'error' ), Promise . resolve ( 2 )];
Promise . allSettled (promises). then (( results ) => console. log (results));
// [ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: 'error' }, { status: 'fulfilled', value: 2 } ]
const promises = [ Promise . resolve ( 1 ), Promise . reject ( 'error' ), Promise . resolve ( 2 )];
Promise . allSettled (promises). then (( results ) => console. log (results));
// [ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: 'error' }, { status: 'fulfilled', value: 2 } ]
??는 왼쪽 값이 null이나 undefined일 때만 오른쪽 값을 돌려줍니다. ||는 0이나 빈 문자열 같은 falsy한 값에도 오른쪽으로 넘어가 버립니다. 함께 들어온 ?.는 중간 값이 null이나 undefined여도 오류 없이 undefined를 돌려줍니다.
const num = 0 ;
console. log (num || 'default' ); // 'default'. 0은 falsy라서 오른쪽으로 넘어간다
console. log (num ?? 'default' ); // 0. null도 undefined도 아니므로 그대로 쓴다
const user = {};
console. log (user.profile?.name); // undefined. 오류가 나지 않는다
const num = 0 ;
console. log (num || 'default' ); // 'default'. 0은 falsy라서 오른쪽으로 넘어간다
console. log (num ?? 'default' ); // 0. null도 undefined도 아니므로 그대로 쓴다
const user = {};
console. log (user.profile?.name); // undefined. 오류가 나지 않는다
늘어놓고 보면 흐름이 보입니다. ES4는 한 번에 너무 많이 바꾸려다 나오지 못했고, ES6는 6년이 걸렸습니다. 2016년부터는 매년 조금씩 바뀝니다. 그래서 새 버전은 한두 가지만 알면 따라갈 수 있습니다. 어떤 문법이 어느 버전에서 들어왔는지 알아 두면, 브라우저 지원 범위나 트랜스파일 설정을 정할 때 판단이 쉬워집니다.