logo

ES2016에서 ES2020까지 추가된 기능들

알아두면 도움되는 ES2016 ~ ES2020 기능들

javascript


여러 가지 일로 바빴던 터라 오랜만에 글을 씁니다. 무엇을 쓸까 고민하다가 올 초에 ES2020이 발표되고 언제 한번 모아서 정리 혹은 공부할 겸 쓰려 했었는데 이제야 하게 되네요.

ES2015(ES6)는 이미 현업에서도 많이 사용되고 알려져 있으니 제외하고 ES2016부터 ES2020까지 빠르게 발전하고 있는 JavaScript의 추가점을 살펴보겠습니다.

ES2016

ECMAScript 2016으로 알려진 ES2016은 2016년 6월에 발표되었습니다. 대규모 업데이트였던 ES2015와 달리 ES2016은 두 가지 기능만 포함한 소규모 수준의 업데이트였습니다.

Array.prototype.includes()

  • 배열이 특정 요소가 존재하는지를 확인합니다.
  • ES6 이하의 경우 indexOf()를 사용했었습니다.
// arr.includes(valueToFind[, fromIndex])
[1, 2, 3].includes(3); // true
[1, 2, 3].includes(3, 3);  // false

Exponentiation Operator

  • 제곱의 값을 구할 경우 사용합니다.
// Math.pow(base, exponent)
Math.pow(2, 10); // 1024
Math.pow(4, 2) == 4 ** 2 // true

ES2017

2017년 6월에 발표되었으며 asyncawait가 바로 이시기에 도입되었습니다.

Async/Await Functions

  • ES6의 Promise 보다 더 유용하며 덜 복잡합니다.
  • 비동기 함수 간 연계를 쉽게 할 수 있으며 가독성은 일반 Promise보다 훨씬 더 좋습니다.
  • await는 항상 async와 함께 사용하여야 합니다.
// async, await
const handleAsync = () =>
  new Promise(resolve => {
    setTimeout(() => resolve('Async'), 1000);
  });

async function run() {
  const result = await handleAsync();
  console.log(result);
}

console.log('Hi');
run();
console.log('Bye');

// Hi -> Bye -> Async

String.prototype.padStart(), String.prototype.padEnd()

  • 문자열에 패딩을 처리합니다.
  • 현재 문자열의 길이보다 작다면 값 변경 없이 그대로 리턴합니다.
// String.prototype.padStart(targetLength [, padString])
'txt'.padStart(10);         // "       txt"
'txt'.padStart(10, "wow");  // "wowwowwtxt"
'txt'.padStart(6,"123465"); // "123txt"
'txt'.padStart(8, "0");     // "00000txt"
'txt'.padStart(1);          // "txt"

// String.prototype.padEnd(targetLength [, padString])
'txt'.padEnd(10);          // "txt       "
'txt'.padEnd(10, "wow");   // "txtwowwoww"
'txt'.padEnd(6, "123456"); // "txt123"
'txt'.padEnd(1);           // "txt"

Object.values()

  • 객체의 value 값을 모은 배열을 리턴합니다.
const obj = {
  a: '1',
  b: '2',
};
Object.values(obj); // ['1', '2']

Object.entries()

  • 모든 객체 소유 속성을 [key, value]의 값의 배열로 모아 리턴합니다.
const obj = {
  a: '1',
  b: '2',
};
Object.entries(obj); // [[ 'a', '1' ], [ 'b', '2' ]] 

Object.getOwnPropertyDescriptors()

  • 객체의 모든 속성 설명을 리턴합니다.
    1. value: 속성 값
    2. writable: true일 경우 속성을 변경할 수 있습니다.
    3. get: 속성을 읽을 때 호출되는 속성에 대한 getter 함수
    4. set: 속성이 값으로 설정 될 때 호출되는 속성에 대한 setter 함수
    5. configurable: false 인 경우 속성을 제거하거나 해당 값을 제외하고 속성을 변경할 수 없습니다.
    6. enumerable: 속성이 열거 가능한 경우 true 입니다.
const obj = {
  key1: 1,
  key2: 2
};

Object.getOwnPropertyDescriptors(obj);
// { key1: { value: 1, writable: true, enumerable: true, configurable: true }, 
//   key2: { value: 2, writable: true, enumerable: true, configurable: true } } 

Trailing Commas

  • 함수 선언 및 함수 호출 시 인자나 매개변수 마지막에 쉼표를 사용할 수 있습니다.
const run = (variable1, variable2,) => {
  //...
}

run('1', '2',);

SharedArrayBuffer

  • WebWorkers는 브라우저에서 다중 스레드 프로그램을 만드는 데 사용되며 이벤트를 통해 메시징 프로토콜을 제공합니다. (병렬처리에 사용되는 방법)
  • WebWorkersworker들은 각각 분리된 전역 환경에서 실행되기 때문에 그들이나 메인 스레드 간의 통신을 간접적인 방법으로(데이터 복사) 처리합니다.
  • 이런 간접적인 방법은 오버헤드 등의 문제를 야기하는데 SharedArrayBuffer 객체는 worker 나 메인 스레드 간에 데이터 공유를 통해 이를 해결할 수 있는 기능을 합니다.
  • SharedArrayBuffer 객체를 사용할 때 읽기 및 쓰기 등을 Atomics 객체를 사용해서 관리합니다.
// create a SharedArrayBuffer with 1024 byte
const buffer = new SharedArrayBuffer(1024);

buffer.byteLength; // 1024

ES2018

2018년 6월에 발표되었습니다. Object에도 RestSpread 문법이 도입되는 등 큰 변화는 아니지만 주목할만한 점들이 몇몇 있었습니다.

Object Rest/Spread Properties

  • 가장 많이 사용되는 기능 중 하나로 ES2015에서 발표된 Array의 Rest/Spread와 동일합니다.
  • 얕은 복제(prototype 제외) 또는 객체의 병합 그리고 Object.assign() 보다 더 짧고 쉬운 문법을 사용합니다.
// Rest
const obj1 = {
  a: 1,
  b: 2,
  c: 3
};
const { a, b, ...rest } = obj1;

a // 1
b // 2
rest // { c: 3 }

// Spread
const items = { a, b, ...rest };
items // { a: 1, b: 2, c: 3 }

Asynchronous Iteration

  • 여러 비동기문을 for await (...of...)를 사용하여 반복작업을 쉽게 다룰 수 있습니다.
  • 포함된 비동기문들을 Promise.all의 경우는 동시적으로 실행되고 for await (...of...)의 경우는 순차적으로 실행됩니다.
(async function() {
  for await (const item of iterableItems) {
    // ...
  }
})();

Promise.prototype.finally()

  • Promise가 성공적으로 전개되면 then()을 호출하고 이 과정에서 실패하면 대신 catch()가 호출됩니다.
  • finally()는 성공 여부와는 관계없이 호출됩니다.
Promise.resolve('start')
  .then((msg) => console.log(msg))
  .catch((err) => console.log(err))
  .finally(() => console.log('finally'));
// start
// finally

RegExp Features

Lookbehind Assertions
  • Lookbehind는 특정 문자열 뒤에 오는 문자열을 찾아줍니다.
  • 이전에는 특정 문자열 앞에 오는 문자열을 찾는 Lookahead만 존재했습니다.
// Positive and Negative lookahead
// X(?=Y): X를 찾지만 Y가 뒤에 오는 경우에만 가능
// X(?!Y): 반대
const lookahead1 = /im(?=possible)/.test('This is impossible.');
const lookahead2 = /im(?!possible)/.test('This is impossible.');
// true
// false

// Positive and Negative lookbehind
// (?<=Y)X: X와 일치하지만, 앞에 Y가 있는 경우에만 가능
// (?<!Y)X: 반대
const lookbehind1 = /(?<=super)hero/.test('Batman is the superhero protector of Gotham City.');
const lookbehind2 = /(?<!super)hero/.test('Batman is the superhero protector of Gotham City.');
// true
// false
s Flag
  • .\n\r을 제외한 모든 문자와 일치하는 정규식 패턴입니다.
  • 추가된 s는 줄 바꿈을 일치하고자 할 때 사용합니다.
/hellow.world/.test('hellow\nworld'); // false
/hellow.world/s.test('hellow\nworld'); // true
Unicode Property Escapes \p{...} and \P{...}
  • 긍정인 \p{...}와 부정인 \P{...}로 유니코드를 확인하는데 사용합니다.
  • 속성확인은 Link 에서 가능합니다.
// Uppercase, Lowercase, White_Space, Alphabetic, Emoji 등
/^\p{Lowercase}$/u.test('a'); // true
/^\p{Emoji}+$/u.test('🙃'); // true
Named Capturing Groups
  • 캡쳐링 그룹은 정규 표현식에서 괄호로 묶은 것을 말합니다.
  • 결과값에 번호로 할당하는 대신 이름을 지정하여 할당 할 수 있습니다.
  • 이는 groups로 접근할 수 있습니다.
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const result = re.exec('2020-09-20');
// result.groups
// { year: '2020', month: '09', day: '20' } 

ES2019

2019년 6월에 발표되었습니다. 변화가 적은 업데이트였습니다.

Array.prototype.flat(), Array.prototype.flatMap()

  • 다차원 배열에서 1차원 배열로 만들 수 있습니다.
  • 지정된 깊이를 선언함으로써 원하는 깊이 만큼 결과를 얻을 수 있습니다.
  • flatMap()map()flat()을 하나로 결합한 것 입니다.
// Array.prototype.flat()
['apple', ['banana', 'mango']].flat();
// [ 'apple', 'banana', 'mango' ]

['apple', ['banana', ['mango']]].flat();
// [ 'apple', 'banana', [ 'mango' ] ]

['apple', ['banana', ['mango']]].flat(2);
// [ 'apple', 'banana', 'mango' ]

['apple', ['banana', ['mango']]].flat(Infinity);
// [ 'apple', 'banana', 'mango' ]

// Array.prototype.flatMap()
['My name', 'is Shin'].map(words => words.split(' '));
// [ [ 'My', 'name' ], [ 'is', 'Shin' ] ]

['My name', 'is Shin'].flatMap(words => words.split(' '));
// [ 'My', 'name', 'is', 'Shin' ]

Object.fromEntries()

  • ES2017에서 추가된 Object.entries() 반대 기능입니다.
  • 파이썬의 dict()함수와 비슷하게 Key/Value의 배열을 객체로 변환합니다.
const obj = { first: 1, second: 2, third: 3 };
const entries = Object.entries(obj);
// [ [ 'first', 1 ], [ 'second', 2 ], [ 'third', 3 ] ] 
const fromEntries = Object.fromEntries(entries);
// { first: 1, second: 2, third: 3 } 

String.prototype.trimStart(), String.prototype.trimEnd()

  • trimLeft()trimRight() 메서드와 동일합니다.
  • padStart()padEnd()와의 일관성 유지를 위해 도입되었습니다.
const greeting = '   Hello world!   ';
greeting.trimStart();
// "Hello world!   ";

greeting.trimEnd();
// "   Hello world!";

Symbol.prototype.description

  • Symbol을 만들 때 description을 추가할 수 있습니다.
const symbol = Symbol('Test Symbol');
symbol.description // Test Symbol

Optional Catch Binding

  • 가끔 try/catch의 catch 블록에 매개 변수를 바인딩 할 필요가 없을때 제외 가능합니다.
try {
  // ...
} catch {
  // handle error
}

Function.prototype.toString()

  • 함수에는 항상 코드를 포함하여 문자열을 리턴하는 toString() 라는 메서드가 있습니다.
  • 기존에는 주석 및 기타 문자 등이 제거된 채 리턴하였으나 이제는 정의 된 대로 정확하게 리턴합니다.
function /* test */ test() {
  // ...
}
// before
test.toString(); //'function test() { // ... }
// after
test.toString(); // 'function /* test */ test () { // ... }'

JSON 개선

  • 이 변경 전에는 JSON으로 분석 된 문자열에서 줄 구분 기호(\u2028) 및 단락 구분 기호(\u2029) 기호가 허용되지 않았습니다.
  • JSON.parse()를 사용하면 해당 문자 때문에 SyntaxError가 발생했지만, 이제는 JSON 표준에 정의 된 대로 올바르게 구문 분석됩니다.

JSON.stringify() 개선

  • 이전 경우 가끔 문자열과 코드, 특히 UTF-8로 표현 할 수 없는 값(U + D800-U + DFFF)을 반환합니다.
  • ES2019에서는 잘못된 형식의 유니코드 문자열을 반환하는 것을 방지합니다.

ES2020

코로나로 어수선한 올해인 2020년 6월에 발표되었습니다. ES2016에서 ES2019까지도 그랬지만 ES2020도 ES2015에 등장한 것만큼 새로운 기능이 포함되어 있지 않습니다.

BigInt

  • JavaScript에서 정수로 저장할 수있는 최대 수는 pow(2, 53) - 1 입니다.
  • BigInt는 그 이상을 처리할 수 있습니다.
  • BigInt()로 선언하거나 숫자 뒤에 n을 붙입니다.
  • BigInt간에 계산이 가능하며 소수점 표현은 불가능합니다.
const number = Number.MAX_SAFE_INTEGER;
// 9007199254740991
// typeof number === 'number'

const bigInt1 = 9007199254740992n;
const bigInt2 = BigInt(9007199254740992);
// 9007199254740992
// typeof bigInt1, typeof bigInt1 === 'bigint'

Dynamic Import

  • 이제 모듈을 동적으로 import 할 수 있습니다.
import('./test.js').then((module) => {
  // ...
});

(async function() {
  const module = await import('./test.js');
  // ...
})();

globalThis

  • 브라우저의 전역객체는 window이고 Node.js의 전역객체는 global입니다.
  • globalThis는 분기가 필요없는 하나로 통합된 전역객체입니다.
  • 기존 windowglobal도 사용 가능합니다.
// Browser
globalThis === window // true;

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

String.prototype.matchAll()

  • 정규식에서 일치하는 모든 그룹을 포함하는 반복자를 리턴합니다.
const regexp = /[a-c]/g;
const result = 'ABC, abc'.matchAll(regexp);
// { [Iterator] }

Array.from(result, val => {
  // val
  // [ 'a', index: 5, input: 'ABC, abc', groups: undefined ] 
  // [ 'b', index: 6, input: 'ABC, abc', groups: undefined ] 
  // [ 'c', index: 7, input: 'ABC, abc', groups: undefined ] 
});

Promise.allSettled()

  • Promise.all()은 요청된 비동기 그룹 중 하나라도 실패할 경우 catch 블록으로 이동합니다.
  • Promise.allSettled()는 성공과 실패여부에 관계없이 모두 응답이 처리 되었을 경우 then 블록으로 이동합니다.
  • 하나의 실패가 다른 요청의 성공에 영향을 미치지 않을 때 사용합니다.
const list = [asyncFunction(), asyncFunction(), asyncFunction()];
Promise.allSettled(list).then(result => {
  // result[0] { status: 'fulfilled', value: 'ok' }
  // result[1] { status: 'rejected', reason: 'wrong data' }
  // result[2] { status: 'fulfilled', value: 'ok' }
});

Nullish Coalescing

  • 기존 || 연산자에서는 falsey값을 모두 확인합니다.
  • ?? 연산자는 nullundefined 값만 확인합니다.
undefined || 'test'; // test
undefined ?? 'test'; // test

null || 'test'; // test
null ?? 'test'; // test

false || 'test'; // test
false ?? 'test'; // false

0 || 'test'; // test
0 ?? 'test'; // 0

'' || 'test'; // test
'' ?? 'test'; // ''

Optional Chaining

  • 존재여부를 정확히 알 수 없는 객체의 값을 구하기 위해선 && 연산자를 사용하여 길게 값을 이어 확인해야 합니다.
  • 추가된 Optional Chaining기법인 ?를 사용하여 짧게 구할 수 있습니다.
const obj = {
  props: {
    values: {
      first: 1,
    }
  }
};

// before
obj && obj.props && obj.props.values && obj.props.values.first

// after
obj?.props?.values?.first

Module Namespace Exports

  • import구문에서만 가능하던 것이 export도 가능하게 되었습니다.
// import
import * as item from './item.js';

// export
export * as item from './item.js';
export { item };

import.meta

  • import.meta객체를 사용하여 모듈에 대한 메타 정보를 얻습니다.
<script type="test" src="test.js"></script>
import.meta; // { url: "file:///home/user/test.js" }

for-in 개선

  • 기존의 ECMA는 for (x in y)의 실행할 순서를 정하지 않았습니다.
  • 브라우저는 지금까지 자체적으로 일관된 순서를 구현했지만 이제 ES2020에서 공식적으로 표준화되었습니다.