자바스크립트 최신 문법 살펴보기 (ES7-ES13)

Kyu·2022년 2월 21일
15
post-thumbnail

ECMASript 2015(ES6) 이후 자바스크립트 기능들을 소개합니다.

용어 설명

Ecma

  • Ecma 인터내셔널(영어: Ecma International)은 정보와 통신 시스템을 위한 국제적 표준화 기구
  • 원래 이름은 European Computer Manufacturers Association (ECMA)이었으나, 기구의 국제적 확장을 반영하여 현재의 이름으로 변경되었다.
  • 지금의 Ecma는 두문자어가 아니며 대문자만 사용하지 않는다.
  • Ecma 인터내셔널은 1961년 유럽에서 컴퓨터 시스템을 표준화하기 위해 세워졌다,
  • ECMA-262 외에도 다수의 표준을 책임지고 있다.

ECMA-262

  • ECMAScript 언어 표준 명세서
  • 자바스크립트는 ECMA-262를 만족하는 구현체를 말한다.
  • Ecma 인터내셔널의 여러 기술 위원회(Technial Committee, 이하 TC) 중 TC39 라는 위원회가 이 명세를 관리한다.

New features

  • ES2016 (ES7)
  • ES2017 (ES8)
  • ES2018 (ES9)
  • ES2019 (ES10)
  • ES2020 (ES11)
  • ES2021 (ES12)
  • ES2022 (ES13) Stage4 / Finished

ES2016 (ES7)

  • ECMAScript 2016은 Ecma TC39의 새로운 연간 릴리스 주기 및 공개 개발 프로세스에 따라 릴리스된 최초의 ECMAScript 에디션
  • 새로운 지수 연산자 추가, Array.prototype에 includes 메서드 추가

Array.prototype.includes()

[1].includes(1); // true 

지수 연산자

2**10 // 1024

ES2017 (ES8)

  • ECMAScript 2017은 Async Functions, Shared Memory, Atomics와 함께 더 작은 언어 및 라이브러리 개선 사항, 버그 수정, 편집 업데이트를 도입함.
  • 비동기 함수는 약속 반환 함수에 대한 구문을 제공하여 비동기 프로그래밍 환경을 개선
  • Object.values, Object.entries 메서드가 추가
  • Object.getOwnPropertyDescriptors 추가

async/await

async getData() {
  const res = await api.getTableData(); // await asynchronous task 
  // do something
}

Object.values()

Object.values({a: 1, b: 2, c: 3}); // [1, 2, 3]

Object.entries()

Object.entries({a: 1, b: 2, c: 3}); // [["a", 1], ["b", 2], ["c", 3]]

Object.getOwnPropertyDescriptors()

  • 개체의 모든 속성에 대한 설명자를 가져오거나 개체의 속성이 없는 경우 빈 개체를 반환.
let user = { name: "John"};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

console.log(JSON.stringify(descriptor, null, 2));
/* property descriptor:
{
  "value": "John",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

SharedArrayBuffer object

  • 멀티 스레드간에 메모리 공유를 위해 사용. 주요 브라우저에서 기본적으로 비활성화 되어있음.
/** 
** @param {*} length The size of the array buffer created, in bytes. 
** @Returns {ShareDarrayBuffer} A new ShareDarrayBuffer object of specified size. Its contents are initialized to 0. */
new SharedArrayBuffer(10)

MDN SharedArrayBuffer
만화로 소개하는 ArrayBuffer 와 SharedArrayBuffer

Atomic Object

  • Atomics 개체는 ShareDarrayBuffer 개체에서 원자적 작업을 수행하는 데 사용되는 정적 메서드 집합을 제공함.

MDN Atomics

String padding

  • 문자열 끝 부분이나 시작 부분을 다른 문자열로 채워 주어진 길이를 만족하는 새로운 문자열을 만들어낼 수 있다.
"hello".padStart(6); // " hello"
"hello".padEnd(6); // "hello "
"hello".padStart(3); // "hello" // 문자열 길이보다 목표 문자열 길이가 짧다면 채워넣지 않고 그대로 반환
"hello".padEnd(20, "*"); // "hello***************" // 사용자가 지정한 값으로 채우는 것도 가능

Trailing Comma

  • JavaScript는 초기부터 배열 리터럴에 trailing comma를 허용했으며, ECMAScript 5부터는 객체 리터럴, ECMAScript 2017부터는 함수의 매개변수에도 허용
function f(p) {}
function f(p,) {}

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

ES2018 (ES9)

  • AsyncIterator 프로토콜 및 비동기 생성기를 통한 비동기 반복 지원 도입 for-await-of
  • 객체 Rest 및 Spread 속성이 포함
  • dotAll또한 플래그, 명명된 캡처 그룹, 유니코드 속성 이스케이프 및 look-behind 어설션 의 4가지 새로운 정규식 기능이 포함

Asynchronous iteration

async function process(array) { 
  for await (let i of array) { 
    // doSomething(i); 
  }
}

Promise.finally()

  • Promise가 처리되면 충족되거나 거부되는지 여부에 관계없이 지정된 콜백 함수가 실행됨.
Promise.resolve().then().catch(e => e).finally();

Rest/Spread

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

const {a, b, ...z} = myObj;
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": 0 }

정규식 기능 개선 및 추가

1) RegExp lookbehind assertions

  • 앞에 오는 항목에 따라 문자열 일치
// ?= 특정 하위 문자열이 뒤에 오는 문자열을 일치시키는데 사용
/Roger(?=Waters)/
/Roger(?= Waters)/.test('Roger is my dog') //false
/Roger(?= Waters)/.test('Roger is my dog and Roger Waters is a famous musician') //true

// ?! 문자열 뒤에 특정 하위 문자열이 오지 않는 경우 일치하는 역 연산을 수행
/Roger(?!Waters)/
/Roger(?! Waters)/.test('Roger is my dog') //true
/Roger(?! Waters)/.test('Roger Waters is a famous musician') //false

// ?<= 새로 추가된 표현식
/(?<=Roger) Waters/
/(?<=Roger) Waters/.test('Pink Waters is my dog') //false
/(?<=Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician') //true

// ?<! 새로 추가된 표현식
/(?<!Roger) Waters/
/(?<!Roger) Waters/.test('Pink Waters is my dog') //true
/(?<!Roger) Waters/.test('Roger is my dog and Roger Waters is a famous musician') //false

2) 유니코드 속성 이스케이프 \p{…}\P{…}

// ASCII
/^\p{ASCII}+$/u.test('abc')   //✅
/^\p{ASCII}+$/u.test('ABC@')  //✅
/^\p{ASCII}+$/u.test('ABC🙃') //❌

// HEX
/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF') //✅
/^\p{ASCII_Hex_Digit}+$/u.test('h')                //❌

// Emoji, ppercase, Lowercase, White_Space, Alphabetic
/^\p{Lowercase}$/u.test('h') //✅
/^\p{Uppercase}$/u.test('H') //✅

/^\p{Emoji}+$/u.test('H')   //❌
/^\p{Emoji}+$/u.test('🙃🙃') //✅

// Script
/^\p{Script=Greek}+$/u.test('ελληνικά') //✅
/^\p{Script=Latin}+$/u.test('hey') //✅

3) 명명된 캡처 그룹(named capture group)

  • ES2018에서는 결과 배열의 슬롯을 할당하는 대신 캡처 그룹을 이름에 할당할 수 있음
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = re.exec('2015-01-02')

// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

4) s 플래그(dotAll)

  • .표현식은 개행문자를 제외한 모든 문자였으나, s플래그를 달면 개행식도 포함하게 된다.
/hi.welcome/.test('hi\nwelcome') // false
/hi.welcome/s.test('hi\nwelcome') // true

ES2019 (ES10)

  • Array.prototype.{flat,flatMap}
  • Optional catch binding
  • Object.fromEntries()
  • String.prototype.{trimStart,trimEnd}
  • Symbol.prototype.description
  • JSON improvements
  • Well-formed JSON.stringify()
  • Function.prototype.toString()

Array.prototype.{flat,flatMap}

// flat()
['Dog', ['Sheep', 'Wolf']].flat()
//[ 'Dog', 'Sheep', 'Wolf' ]

// 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"]

선택적 catch 할당(Optional catch binding)

try {
  //...
} catch /*(e)*/ {
  //handle error
}

Object.fromEntries()

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

const obj = Object.fromEntries(entries);

console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }

String.prototype.{trimStart,trimEnd}

// trimStart()
'Testing'.trimStart() //'Testing'
' Testing'.trimStart() //'Testing'
' Testing '.trimStart() //'Testing '
'Testing '.trimStart() //'Testing '

// trimEnd()
'Testing'.trimEnd() //'Testing'
' Testing'.trimEnd() //' Testing'
' Testing '.trimEnd() //' Testing'
'Testing '.trimEnd() //'Testing'

Symbol.prototype.description

  • 심벌 객체의 description은 해당 심벌 객체의 설명을 반환한다.
Symbol('desc').toString();   // "Symbol(desc)"
Symbol('desc').description;  // "desc"

JSON 개선사항(JSON improvements)

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

Well-formed JSON.stringify()

  • 잘못된 형식의 유니코드 문자열을 반환하지 않도록 JSON.stringify를 변경되었다.
  JSON.stringify("\uD800"); // 변경 전 --> '"�"' // 잘못된 형식의 유니코드 문자를 반환
  JSON.stringify("\uD800"); // 변경 후 --> '"\ud800"' // 유효한 유니코드를 반환

Function.prototype.toString()

  • toString() 메서드는 함수의 소스 코드를 나타내는 문자열을 반환한다.
  • ES2016까지는 소스 코드에서 주석이나 공백 문자를 제거했지만, ES2019에서
    개정되어 문자열에 주석 등도 포함된다.
function /* this is bar */ bar() {}

// old
bar.toString() //'function bar() {}

// new
bar.toString() // 'function /* this is bar */ bar () {}'

ES2020 (ES11)

  • BigInt
  • Dynamic Import
  • Optional Chaining
  • Promise.allSettled
  • Nullish Coalescing
  • String.protype.matchAll
  • Module Namespace Exports
  • import.meta
  • globalThis

BigInt

  • BigInt는 정수 리터럴의 뒤에 n을 붙이거나(10n) 함수 BigInt()를 호출해 생성할 수 있다.

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

const theBiggestInt = 9007199254740991n;
const bigintSum = theBiggestInt + 1n; // 9007199254740992n
const alsoHuge = BigInt(9007199254740991); // 9007199254740991n
typeof bigintSum // "bigint"

Dynamic Import

  • ES2020 부터는 필요할 때 모듈을 동적으로 가져올 수 있다.
if (condition1 && condition2) {
  const module = await import('./path/to/module.js');
  module.doSomething();
}

Optional Chaning

  • ?. 연산자는 . 체이닝 연산자와 유사하게 작동하지만, 만약 참조가 nullish(null 또는 undefined)이라면, 에러가 발생하는 것 대신에 표현식의 리턴 값은 undefined로 단락된다. 함수 호출에서 사용될 때, 만약 주어진 함수가 존재하지 않는다면, undefined를 리턴한다.
const adventurer = {
  name: 'Alice',
  cat: { name: 'Dinah' }
};

const catName = adventurer.cat?.name; // 'Dinah'
const dogName = adventurer.dog?.name; // undefined

Promise.allSettled

  • Promise.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"
  }
]

Nullish Coalescing Operator

  • 널 병합 연산자 (??) 는 왼쪽 피연산자가 null 또는 undefined일 때 오른쪽 피연산자를 반환하고, 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자이다.
let user = { u1: 0, u2: false, u3: null, u4: undefined u5: '', }
let u1 = user.u1 ?? 'user 1' // 0
let u2 = user.u2 ?? 'user 2' // false 
let u3 = user.u3?? 'user 3' // user 3 
let u4 = user.u4?? 'user 4' // user 4
let u5 = user.u5?? 'User 5' // ''

String.protype.matchAll

  • matchAll 메서드는 지정된 정규식에 대해 문자열과 일치하는 모든 결과의 iterator를 반환하는 메서드입니다.(캡쳐링 그룹 포함) 정규 표현식 뒤에 g flag를 사용해주어야 합니다.
const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
const array = [...str.matchAll(regexp)];

console.log(array[0]); // expected output: Array ["test1", "e", "st1", "1"]
console.log(array[1]); // expected output: Array ["test2", "e", "st2", "2"]

Module Namespace Exports

export * as A from "./moduleA";
export * as B from "./moduleA";

import { A, B } from "./modules";
console.log(A.a); // 1
console.log(A.b); // 2
console.log(B.b); // 3
console.log(B.c); // 4

import.meta

  • 현재 모듈파일의 메타정보를 가져올 수 있다.
<script type="module" src="my-module.js">
console.log(import.meta); // { url: "file:///home/user/my-module.js" }

globalThis

  • globalThis는 환경에 관계없이 전역객체를 통일된 방법으로 참조할 수 있는 방법이다.
// browser environment
console.log(globalThis);    // => Window {...}

// node.js environment
console.log(globalThis);    // => Object [global] {...}

// web worker environment
console.log(globalThis);    // => DedicatedWorkerGlobalScope {...}

ES2021 (ES12)

  • String.prototype.replaceAll()
  • Promise.any()
  • WeakRefs
  • Logical assignment operators (논리 할당 연산자)
  • Numeric separators (숫자 구분 기호)
  • Array.prototype.sort 개선

String.prototype.replaceAll()

  • 정규식에 g옵션을 통해 전역으로 적용하지 않고도 문자열의 지정한 모든 문자열을 특정 문자열의 값으로 변경한다.
const str = 'hello world';
str.replaceAll('l', ''); // "heo word"

Promise.any()

  • Promise 반복 가능 객체를 수신하고 Promise 중 하나가 성공할 때마다 성공한 Promise를 반환한다. iterable 객체의 Promise 중 어느 것도 성공하지 못하면(즉, 모든 Promise가 실패/거부됨) 실패한 Promise가 반환된다.
const anySuccessPromises = [
	new Promise((res, rej) => setTimeout(res, 200, 'first')),
	new Promise((res, rej) => setTimeout(rej, 100, 'second')),
	new Promise((res, rej) => setTimeout(res, 300, 'third')),
];

// first
Promise.any(anySuccessPromises)
.then(value => console.log(value))
.catch(error => console.error(error));

const allFailurePromises = [
	new Promise((res, rej) => setTimeout(rej, 100, 'first')),
	new Promise((res, rej) => setTimeout(rej, 200, 'second')),
	new Promise((res, rej) => setTimeout(rej, 300, 'third')),
];

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

WeakRefs

  • WeakRef개체를 사용하면 해당 개체가 가비지 수집되는 것을 방지하지 않고 다른 개체에 대한 약한 참조를 유지할 수 있다.
class Counter {
  constructor(element) {
    // Remember a weak reference to the DOM element
    this.ref = new WeakRef(element);
    this.start();
  }

  start() {
    if (this.timer) {
      return;
    }

    this.count = 0;

    const tick = () => {
      // Get the element from the weak reference, if it still exists
      const element = this.ref.deref();
      if (element) {
        element.textContent = ++this.count;
      } else {
        // The element doesn't exist anymore
        console.log("The element is gone.");
        this.stop();
        this.ref = null;
      }
    };

    tick();
    this.timer = setInterval(tick, 1000);
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0;
    }
  }
}

const counter = new Counter(document.getElementById("counter"));
setTimeout(() => {
  document.getElementById("counter").remove();
}, 5000);

WeakRefs와 Finalizers 위주로 정리해본 ES2021
약한 참조와 Finalizer

Logical assignment operators (논리 할당 연산자)

// before
obj.prop = obj.prop || foo(); // obj.prop이 잘못된 값일 경우 할당
obj.prop = obj.prop && foo(); // obj.prop이 올바른 값일 경우 할당
obj.prop = obj.prop ?? foo(); // obj.prop이 null이나 undefined일 경우 할당

// after
obj.prop ||= foo();
obj.prop &&= foo();
obj.prop ??= foo();

Numeric separators (숫자 구분 기호)

// before
10000000000 // 100억
// after
10_000_000_000 // 100억
console.log(10_000_000_000); // 10000000000

Array.prototype.sort 개선

  • sort는 implementation-defined로 기본적인 스펙만 제공하고 나머지는 브라우저에게 맡겼기 때문에 브라우저마다 구현이 달라서 정렬 결과가 다른 문제가 있었다.
  • 이 스펙을 좀 더 정교하게 정의해서 브라우저마다 다를 수 있는 경우의 수를 줄였다.

ES2022 (ES13)

ES2022는 2022년 6월에 출시되며 2022년 3월까지 4단계에 도달한 모든 아이디어는 JavaScript에 포함될 예정이다.

  • Top-level Await Operator
  • Class Field Declarations
  • Private Methods and Fields
  • Static Class Fields and Private Static Methods
  • Regexp Match Indices
  • Ergonomic Brand Checks for Private Fields
  • at() Function for Indexing
  • Scalable Object.prototype.hasOwnProperty()
  • Temporal Function

TC39 Stage 4 / Finished

Top-level Await Operator

  • 이전에는 비동기 함수 외부에서 await 키워드를 선언할 수 없어 오류가 발생했다. 이제 비동기 함수 및 클래스 외부에서 await 연산자를 선언하여 동기화 문제를 해결할 수 있다.
import {getUser} from "./data/User"
let user = await getUser();

Class Field Declarations

  • 처음에는 생성자 내에서만 클래스 필드를 선언할 수 있었지만 이제는 4단계의 제안을 사용하여 생성자를 호출할 필요없이 클래스 자체에서 선언할 수 있다.
class hello {
  fields = 0;
  title;
}

Private Methods and Fields

  • # 기호를 접두사로 사용하여 private 클래스 필드를 직접 선언할 수 있다.
  • 동일한 기호를 사용하여 메서드와 접근자를 비공개로 설정할 수 있으며 동시에 getter 및 setter 메서드를 사용할 수도 있다.
class hello {
  fields = 0;
  #title;
  
  get #title() { return #title; }
  set #title() { #title = null; }
}

Static Class Fields and Private Static Methods

  • static 키워드를 사용하여 정적 클래스 필드와 개인 정적 메서드를 선언하는 방법을 제공
class hello {
  name;
  static title = 'here';
  static get title() { return title; }
}

Regexp Match Indices

  • d문자를 활용하여 일치하는 문자열의 시작 및 끝 인덱스가 있는 배열을 얻을 수 있다.
const re1 = /a+(?<Z>z)?/d;

// indices are relative to start of the input string:
const s1 = "xaaaz";
const m1 = re1.exec(s1);
m1.indices[0][0] === 1;
m1.indices[0][1] === 5;
s1.slice(...m1.indices[0]) === "aaaz";

Ergonomic Brand Checks for Private Fields

  • in 연산자를 사용하여 특정 클래스에 필드가 있는지 여부를 간단히 확인할 수 있다.

class hello{
  name;
  #title;
    get #title() {
    return #title;
  }
  set #title() {
  	#title=null;
  }
  static hasTitle(obj1) {
    return #title in obj1;
  }
}

at() Function for Indexing

  • at() 함수를 사용하여 양수 및 음수 인덱스를 모두 사용하여 문자열을 인덱싱할 수 있다.
array= [1.2.4.5]
console.log(array[array.length-1]);
console.log(array.at(-1)); 

Scalable Object.prototype.hasOwnProperty()

  • Object.prototype.hasOwnProperty.call()과 동일한 hasOwn() 추가
const obj1={ hello:'Hi' }
let hasHello1=Object.prototype.hasOwnProperty.call(obj1, 'hello');
let hasHello= obj1.hasOwn(obj1, 'hello'); // 
console.log(hasHello);
console.log(hasHello1);

Temporal Function (stage 3)


참고

ES6 이후의 변경점 총정리
New features for JS syntax ES6, ES7, ES8, ES9, ES10, ES11, ES12
what's new in es2022?

0개의 댓글