[ECMAScript] [번역] ECMAScript 2016, 2017, and 2018

ECMAScript 2016, 2017 및 2018의 새로운 기능 Example

Imagem de capa

Here are examples of everything new in ECMAScript 2016, 2017, and 2018

JavaScript 의 새로운 기능(ECMAScript)을 추적하기가 어렵습니다. 그리고 유용한 코드 예제를 찾기가 더 어렵습니다.

따라서 이 기사에서는 ES2016, ES2017 및 ES2018(최종 초안)에 추가된 TC39의 완성된 제안서에 나열된 18가지 기능을 모두 다루고 유용한 예를 보여 드리겠습니다.

이것은 꽤 긴 글이지만 읽기 쉬울 것입니다. 이것을 “Netflix binge reading.”이라고 생각해 보세요. 이 글을 다 읽은 후에는 이 모든 기능에 대해 많은 지식을 가질 것을 약속합니다.

좋아요, 하나씩 살펴봅시다.

ECMAScript 2016

1. Array.prototype.includes

includes은 배열의 간단한 인스턴스(instance) 메서드로, 항목이 배열에 있는지 여부를 쉽게 확인할 수 있습니다(indexOf와 다르게 NaN 포함).

const arr = [1, 2, 3 ,4, NaN];

//Instead Of
if(arr.indexOf(3) >= 0) {
    console.log(true)
}

//Use
if(arr.includes(3)) {
    console.log(true)
}

//PS: Note the indexOf doesn’t work for searching NaN
arr.includes(NaN) //true
arr.indexOf(NaN) // -1 (doesn’t work for NaN)

Trivia: 자바 스크립트 스펙의 사람들은 ‘contains’라는 이름을 지정하려고했지만 Mootools는 이미 이것을 사용 했으므로 ‘includes’를 사용했습니다.


2. Exponentiation infix operator

더하기와 빼기와 같은 산술 연산은 각각 +- 과 같은 Infix 연산자를 가집니다. 그들과 마찬가지로, ** infix 연산자는 일반적으로 지수 작동에 사용됩니다. ECMAScript 2016에서 **Math.pow 대신 도입되었습니다.

//Instead of ..
Math.pow(7, 2) // 49

//Use
7**2  //49


ECMAScript 2017

1. Object.values()

Object.values()Object.keys()와 비슷한 새로운 함수이지만 프로토 타입 체인의 값을 제외한 Object 자체 속성의 모든 값을 반환합니다.

const cars = { BMW: 3, Tesla: 2, Toyota: 1 };

//ES2015
//Instead of..
const vals = Object.keys(cars).map(key => cars[key]);
console.log(vals); // [3, 2, 1]

//ES2017 and onwards
//Use..
const values = Object.values(cars);
console.log(values); // [3, 2, 1]


2. Object.entries()

Object.entries()Object.keys()와 관련이 있지만 키만 반환하는 대신 배열 방식으로 키와 값을 반환합니다. 이렇게 하면 루프에서 객체를 사용하거나 객체를 Map로 변환하는 작업을 매우 간단하게 수행 할 수 있습니다.

Example1 :

const cars = { BMW: 3, Tesla: 2, Toyota: 1 };

//ES 5.1
//Instead of extracting keys and then again looping
Object.keys(cars).forEach(function(key){
    console.log('key : ' + key + 'value: ' + cars[key]);
});

//ECMAScript 2017(ES8)
//Use Object.entries()
for(let [key, value] of Object.entries(cars)) {
    console.log( `key : ${key} value: ${value}`);
}

Example2 :

const cars = { BMW: 3, Tesla: 2, Toyota: 1 };

//ES2015
//Instead of keys and then add each item to Map in a loop
const map1 = new Map();
Object.keys(cars).forEach(key => {
    map1.set(key, cars[key]);
});

console.log(map1); // Map { ‘BMW’ => 3, ‘Tesla’ => 2, ‘Toyota’ => 1 }

//ES2017 and onwards
//Use
const map = new Map(Object.entries(cars));

console.log(map);  // Map { ‘BMW’ => 3, ‘Tesla’ => 2, ‘Toyota’ => 1 }


3. String padding

String.prototype.padStartString.prototype.padEnd 라는 두 가지 인스턴스 메서드가 String 에 추가되어 빈 문자열이나 다른 문자열을 원래 문자열의 시작 또는 끝에 appending/prepending 할 수 있습니다.

'someString'.padStart(numberOfCharcters [,stringForPadding]); 
'5'.padStart(10) // '          5'
'5'.padStart(10, '=*') //'=*=*=*=*=5'
'5'.padEnd(10) // '5         '
'5'.padEnd(10, '=*') //'5=*=*=*=*='


3.1 padStart example:

아래 예에서, 우리는 다양한 길이의 숫자들을 가지고 있습니다. 우리는 모든 item의 길이가 10자리로 같도록 “0” 앞에 붙이기를 원합니다. padStart(10, '0')를 사용하면 쉽게 얻을 수 있습니다.

//ECMAScript 2017
//If you have a list of items of varying lengths and want to
//format them for display purposes, you can use padStart
const formatted = [0, 1, 12, 123, 1234, 12345].map(num => 
    num.toString().padStart(10, '0') //adds ‘0’ until Len is 10
);

console.log(formatted);
//prints..
// [
//     ‘0000000000’,
//     ‘0000000001’,
//     ‘0000000012’,
//     ‘0000000123’,
//     ‘0000001234’,
//     ‘0000012345’,
// ];


3.2 padEnd example:

PadEnd는 다양한 길이의 여러 item을 찍어내고 정확히 오른쪽 정렬 하려는 경우에 매우 유용합니다. 아래의 예는 padEnd , padStartObject.entries가 모두 어떻게 조합되어 아름다운 출력을 생성하는지에 대한 좋은 현실적인 예입니다.

const cars = {
  '🚙BMW': '10',
  '🚘Tesla': '5',
  '🚖Lamborghini': '0'
}
Object.entries(cars).map(([name, count]) => {
  //padEnd appends ' -' until the name becomes 20 characters
  //padStart prepends '0' until the count becomes 3 characters.
  console.log(`${name.padEnd(20, ' -')} Count: ${count.padStart(3, '0')}`)
});
//Prints..
// 🚙BMW - - - - - - -  Count: 010
// 🚘Tesla - - - - - -  Count: 005
// 🚖Lamborghini - - -  Count: 000


3.3 ⚠ padStart and padEnd on Emojis and other double-byte chars

Emojis 및 기타 double 바이트 문자는 유니코드의 여러 바이트를 사용하여 표시됩니다. 따라서 padStart 및 padEnd가 예상대로 작동하지 않을 수 있습니다!⚠️ 예를 들어, 우리는 ❤️ 그림 이모티콘을 사용하여 10 자까지 하트 문자열을 채우려한다고 가정 해 봅시다. 결과는 다음과 같습니다.

//Notice that instead of 5 hearts, there are only 2 hearts and 1 heart that looks odd!
'heart'.padStart(10, "❤️"); // prints.. '❤️❤️❤heart'

이것은 ❤️이 2 코드 포인트 길이 ( '\ u2764 \ uFE0F')이기 때문입니다! heart라는 단어 자체는 5 자이므로 총 5 개의 문자만 남게됩니다. 그래서 JS는 '\ u2764 \ uFE0F'를 사용하여 두 개의 하트를 채우고 ❤️❤️을 생성합니다. 마지막 하나는 단순히 heart의 첫 번째 바이트 \u2764를 사용하여 ❤를 생성합니다.

그래서 결국 ❤️❤️❤heart 이렇게 됩니다.


4. Object.getOwnPropertyDescriptors

이 메서드는 주어진 객체의 모든 속성에 대한 모든 세부 정보 (getter get 및 setter set 메서드 포함)를 반환합니다. 이것을 추가하는 주된 동기는 객체를 다른 객체로 얕은 복사 / 복제 할 수 있도록 허용하는 것입니다. 이 객체는 Object.assign과 달리 getter 및 setter 함수도 복사합니다.

Object.assign 은 원래 소스 객체의 getter 및 setter 함수를 제외한 모든 세부 정보를 얕은복사 합니다.

아래 예제에서는 원본 객체 Car를 새 객체 ElectricCar 로 복사 하기위해 Object.defineProperty와 함께 사용하여 Object.assignObject.getOwnPropertyDescriptors 간의 차이를 보여줍니다. Object.getOwnPropertyDescriptors를 사용하면 , discount 의 getter 및 setter 함수도 대상 객체에 복사됩니다.

//BEFORE...
var Car = {
    name: 'BMW',
    price: 1000000,
    set discount(x) {
        this.d = x;
    },
    get discount() {
        return this.d;
    },
};

//Print details of Car object's 'discount' property
console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));

//prints..
// { 
//   get: [Function: get],
//   set: [Function: set],
//   enumerable: true,
//   configurable: true
// }

//Copy Car's properties to ElectricCar using Object.assign
const ElectricCar = Object.assign({}, Car);

//Print details of ElectricCar object's 'discount' property
console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'));
//prints..
// { 
//   value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true 
  
// }
//⚠️Notice that getters and setters are missing in ElectricCar object for 'discount' property !👎👎
//AFTER...
var Car = {
    name: 'BMW',
    price: 1000000,
    set discount(x) {
        this.d = x;
    },
    get discount() {
        return this.d;
    },
};

//Copy Car's properties to ElectricCar2 using Object.defineProperties 
//and extract Car's properties using Object.getOwnPropertyDescriptors
const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car));

//Print details of ElectricCar2 object's 'discount' property
console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount'));
//prints..
// { get: [Function: get],  👈🏼👈🏼👈🏼
//   set: [Function: set],  👈🏼👈🏼👈🏼
//   enumerable: true,
//   configurable: true 
// }
// Notice that getters and setters are present in the ElectricCar2 object for 'discount' property!


5. Add trailing commas in the function parameters

이 업데이트는 마지막 함수 매개변수 이후에 trailing commas가 오도록 허용하는 마이너 업데이트 입니다. 마지막 개발자만의 코드만을 확인하기 위한 git blame과 같은 도구를 도와주기 위해서입니다.

다음 예에서는 문제와 해결 방법을 보여줍니다.

// The problem

//Developer #1 creates this
function Person (
    name,
    age // <— Trailing comma after age param throws error
    ) {
        this.name = name;
        this.age = age;
}

// Developer #2 adds address parameter
function Person (
    name,
    age, // adds a comma <—— because of comma, developer #1 will be blamed by tools like git etc
    address // adds this new parameter
    ) {
        this.name = name;
        this.age = age;
        this.address = address; // adds this line
}

// Solution ECMAScript 2017
// Allow Developer #1 to add a trailing comma
// Developer #1 creates this
function Person (
    name,
    age, // <— because of trailing comma, Developer #2 doesn’t need to change this line
    ) {
        this.name = name;
        this.age = age;
}

참고 : trailing commas로 함수를 호출할 수도 있습니다.


6. Async/Await

이것은 단연코 가장 중요하고 유용한 기능입니다. 비동기 함수는 콜백 지옥을 다루지 않고 전체 코드를 단순하게 보이게 만듭니다.

async 키워드는 Javascript 컴파일러에게 함수를 다르게 처리하도록 지시합니다. 컴파일러는 해당 함수 내에서 await 키워드에 도달할 때 마다 일시 중지됩니다. 이는 await 라는 표현 후에 promise 가 반환되고 promise 가 resolve 되거나 reject 될때까지 기다린 후 계속 이어진다고 가정합니다.

아래 예에서 getAmount 함수는 두 비동기 함수 getUser과 getBankBalance를 호출합니다. 이를 promise로 할 수 있지만 async await를 사용하는 것이 더 우아하고 간단합니다.

//Instead of ..
//ES2015 Promise
function getAmount(userId){
    getUser(userId)
        .then(getBankBalance)
        .then(amount => {
            console.log(amount);
        });
}

//Use..
//ES2017
async function getAmount2(userId){
    var user = await getUser(userId);
    var amount = await getBankBalance(user);
    console.log(amount);
}

getAmount('1'); // $1,000
getAmount2('2'); // $1,000

function getUser(userId) {
    return new Promise(resolve => {
        setTimeout(() => {}{
            resolve('John');
        }, 1000);
    });
}

function getBankBalance(user){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (user == 'John') {
                resolve('$1,000');
            } else {
                reject('unknown user');
            }
        }, 1000);
    });
 }


6.1 Async functions themselves return a Promise.

비동기 함수의 결과를 기다리는 경우 Promise 의 then 구문을 사용하여 결과를 캡쳐해야합니다.

다음 예에서는 doubleAndAdd 에 포함되지 않고 console.log 를 사용하여 로그를 찍으려고 합니다. 따라서 기다렸다가 then 구문을 사용하여 결과를 console.log 에 전달합니다.

//Async functions themselves return a Promise!
async function doubleAndAdd(a, b){
    a = await doubleAfter1Sec(a);
    b = await doubleAfter1Sec(b);
    return a + b;
}

//Usage:
doubleAndAdd(1, 2).then(console.log);

function doubleAfter1Sec(param) {
    return new Promise(resolve => {
        setTimeout(resolve(param * 2), 1000);
    });
}


6.2 Calling async/await in parallel

앞의 예제에서 우린 두 번 await 를 호출했지만, 매번 1초를 기다립니다(총 2초). 그 대신에 Promise.all 을 사용하여 a 와 b는 각각 서로 다른 것에 종속되지 않으므로 병렬화를 할 수 있습니다.

//Async functions themselves return a Promise!
async function doubleAndAdd(a, b){
    //Notice that I’m using Promise.all
    //Also notice the use of Array restructuring! To capture the result
    [a, b] = await Promise.all([doubleAfter1Sec(a), doubleAfter1Sec(b)]);
    
    return a + b;
}

//Usage:
doubleAndAdd(1, 2).then(console.log);

function doubleAfter1Sec(param) {
    return new Promise(resolve => {
        setTimeout(resolve(param * 2), 1000);
    });
}


6.3 Error handling async/await functions

비동기식 await를 사용할 때 오류를 처리하는 다양한 방법이 있습니다.

Option 1 — Use try catch within the function

//Option 1 - Use try catch within the function
async function doubleAndAdd(a, b) {
    try {
        a = await doubleAfter1Sec(a);
        b = await doubleAfter1Sec(b);
    } catch (e) {
        return NaN; //return something
    }
    
    return a + b;
}

//🚀Usage:
doubleAndAdd('one', 2).then(console.log); // NaN
doubleAndAdd(1, 2).then(console.log); // 6

function doubleAfter1Sec(param) {
    return new Promise((resolve, reject) => {
        setTimeout(function() {
            let val = param * 2;
            isNaN(val) ? reject(NaN) : resolve(val);
        }, 1000);
    });
}

Option 2— Catch every await expression

모든 await 표현은 Promise 를 반환하므로 각 행에서 오류를 확인할 수 있습니다.

//Option 2 - *Catch* errors on  every await line
//as each await expression is a Promise in itself
async function doubleAndAdd(a, b) {
    a = await doubleAfter1Sec(a).catch(e => console.log('"a" is NaN')); // 👈
    b = await doubleAfter1Sec(b).catch(e => console.log('"b" is NaN')); // 👈
    if (!a || !b) {
        return NaN;
    }
    return a + b;
}

//🚀Usage:
doubleAndAdd('one', 2).then(console.log); // NaN  and logs:  "a" is NaN
doubleAndAdd(1, 2).then(console.log); // 6
function doubleAfter1Sec(param) {
    return new Promise((resolve, reject) => {
        setTimeout(function() {
            let val = param * 2;
            isNaN(val) ? reject(NaN) : resolve(val);
        }, 1000);
    });
}

Option 3 — Catch the entire async-await function

//Option 3 - Dont do anything but handle outside the function
//since async / await returns a promise, we can catch the whole function's error
async function doubleAndAdd(a, b) {
    a = await doubleAfter1Sec(a);
    b = await doubleAfter1Sec(b);
    return a + b;
}

//🚀Usage:
doubleAndAdd('one', 2)
        .then(console.log)
        .catch(console.log); // 👈👈🏼<------- use "catch"

function doubleAfter1Sec(param) {
    return new Promise((resolve, reject) => {
        setTimeout(function() {
            let val = param * 2;
            isNaN(val) ? reject(NaN) : resolve(val);
        }, 1000);
    });
}


ECMAScript 2018

ECMAScript는 현재 최종 초안 상태이며 2018년 6월 또는 7월에 출시될 예정입니다. 아래에 설명된 모든 기능은 4단계이며, ECMAScript 2018의 일부입니다.

1. 공유 메모리 및 Atomics

이 기능은 JS 엔진의 핵심 기능이며 매우 향상된 기능입니다.

주요 아이디어는 멀티스레딩 기능을 지원하는 것입니다. 그래서 JS 엔진이 메모리를 관리하도록 하는 대신 개발자가 직접 메모리를 관리함으로써 JS 개발자들이 미래에 고성능 동시 프로그램을 작성할 수 있도록 하는 것입니다.

이 작업은 공유 메모리 공간에 기본적으로 데이터를 저장하는 SharedBBuffer 라는 새로운 유형의 전역 객체를 통해 수행됩니다. 따라서 이 데이터는 메인 JS 스레드와 web-worker 스레드 간에 공유될 수 있습니다.

지금까지는 만약 우리가 메인 JS 스레드와 web-worker 사이에서 데이터를 공유하고자 한다면, 데이터를 복사해서 `postMessage를 사용하여 다른 스레드로 보내야 했습니다. 이제 더 이상은 그렇지 않아도 됩니다.

SharedArrayBuffer 를 사용하면 메인 스레드와 여러 web-worker 스레드를 통해 즉시 데이터에 액세스할 수 있습니다.

그러나 스레드 간에 메모리를 공유하는 것은 경쟁 상태를 유발할 수 있습니다. 경쟁 상태를 피하기 위해 “Atomics” 전역 객체가 도입되었습니다. Atomics 는 스레드가 데이터를 사용할 때 공유 메모리를 잠그는 다양한 메소드를 제공합니다. 또한 공유 메모리에서 이러한 데이터를 안전하게 업데이트하는 메소드를 제공합니다.


2. 태그가 지정된 템플릿 문자 제한 제거

먼저 “태그가 지정된 템플릿 문자”가 무엇인지 명확히 이해해야 이 기능을 더 잘 이해할 수 있습니다. ES2015+ 에는 태그가 지정된 템플릿이라는 기능이 있어서 개발자가 문자열 간 보간 방법을 사용자 지정할 수 있습니다. 예를 들어 표준 방식으로 문자열이 다음과 같이 보간됩니다.

// Standard string literal interpolation
const firstName = "Raja";
const greetings = `Hello ${firstName}`;
console.log(greetings);

태그가 지정된 리터럴에서 함수를 작성하여 문자열의 하드 코딩된 부분을 리터럴(예: [ ‘Hello ‘, ‘!’ ], [ 'Raja'], greet)를 받고 해당 사용자 지정 함수에서 원하는 부분을 반환할 수 있습니다.

아래의 예는 우리의 사용자 정의 “태그” 함수 greet 이 “Good Morning!”, “Good Afternoon”과 같이 하루의 시간에 따라 문자열에 리터럴을 입력하고 사용자 지정 문자열을 반환한다는 것을 보여줍니다.

//A "Tag" function returns a custom string literal.
//In this example, greet calls timeGreet() to append Good 
//Morning/Afternoon/Evening depending on the time of the day.
function greet(hardCodedPartsArray, ...replacementPartsArray) {
    console.log(hardCodedPartsArray); //[ 'Hello ', '!' ]
    console.log(replacementPartsArray); //[ 'Raja' ]
    
    let str = '';
    hardCodedPartsArray.forEach((string, i) => {
        if (i < replacementPartsArray.length) {
            str += `${string} ${replacementPartsArray[i] || ''}`;
        } else {
            str += `${string} ${timeGreet()}`; //<-- append Good morning/afternoon/evening here
        }
    });
    return str;
}

//🚀Usage:
const firstName = 'Raja';
const greetings = greet`Hello ${firstName}!`; //👈🏼<-- Tagged literal

console.log(greetings); //'Hello  Raja! Good Morning!' 🔥

function timeGreet() {
    const hr = new Date().getHours();
    return hr < 12
        ? 'Good Morning!'
        : hr < 18 ? 'Good Afternoon!' : 'Good Evening!';
}

이제 “태그된” 함수가 무엇인지 논의했으므로 많은 사람들이 다른 도메인에서 이 기능을 사용하고 싶어 할 것입니다. 예를 들어 URI를 구성하기 위한 HTTP 요청이나 터미널에서 명령어를 구성하기 위해서 등등에 말입니다.

⚠️The problem with Tagged String literal

문제는 ES2015 및 ES2016 규격에서는 ‘\u00A9’, ‘\u{2F804}’, “\xA9” 와 같은 \u(유니코드), \x(16진수) 이스케이프 문자를 사용할 수 없다는 것입니다.

따라서 내부적으로 다른 도메인의 규칙에 사용되는 Tagged 함수가 있는 경우, \u0049 or \u{@F804} 처럼 보이지 않는 \ubla123abla 규칙을 사용해야 할 수 있습니다.

ES2018에서는 Tagged 함수가 “cooked” 속성과 “raw” 속성이 있는 객체의 값을 반환하는 한, 잘못된 것으로 보이는 이스케이프 문자를 허용하도록 규칙이 완화됩니다.

function myTagFunc(str) { 
    return { "cooked": "undefined", "raw": str.raw[0] }
} 

var str = myTagFunc `hi \ubla123abla`; //call myTagFunc

str // { cooked: "undefined", raw: "hi \\unicode" }


3. “dotall” flag for Regular expression

현재 RegEx에서 점(“.”)은 단일 문자와 일치해야 하지만 \n \r \f 등과 같은 새 줄 문자와 일치하지 않습니다. 예를 들어 :

//Before
/first.second/.test('first\nsecond'); //false

이 향상 기능을 통해 점 연산자는 모든 단일 문자와 일치할 수 있습니다. 이 문제가 발생하지 않도록 하려면 RegEx를 생성할 때 \s 플래그를 사용해야 합니다.

//ECMAScript 2018
/first.second/s.test('first\nsecond'); //true   Notice: /s 👈🏼

다음은 제안 문서의 전체 API입니다.

const re = /foo.bar/s;

re.test("foo\nbar"); // -> true
re.dotAll // -> true
re.flags // -> "s"


4. RegExp Named Group Captures 🔥

이 향상된 기능은 Python, Java 등의 다른 언어에서 “명명된 그룹”으로 불리는 유용한 RegExp 기능을 제공합니다. 이 기능을 통해 개발자는 RegExp를 작성하여 RegExp에 있는 그룹의 다른 부분에 대한 이름(? <name>...)을 제공할 수 있습니다. 그런 다음 이 이름을 사용하여 필요한 그룹을 손쉽게 잡을 수 있습니다.

4.1 기본 명명된 그룹 예제

아래 예에서 우리는 RegEx의 다른 부분을 그룹화하기 위해 (?<year>) (?<month>) (?<day>) 이름을 사용하고 있습니다. 이제 결과 객체에는 연도, 요일의 속성을 가진 그룹 속성이 해당 값으로 포함됩니다.

//BEFORE
let re1 = /(\d{4})-(\d{2})-(\d{2})/;
let result1 = re1.exec("2015-01-02");
console.log(result1);
//Prints ..
//["2015-01-02", "2015", "01", "02", index : 0, input : "2015-01-02", groups: undefined]


//AFTER(ECMAScript 2018)
let re2 = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result2 = re2.exec("2015-01-02");
console.log(result2);
//Prints //
// ['2015-01-02', '2015', '01', '02', index: 0, input: '2015-01-02',
//      groups: { year: '2015', month: '01', day: '02'}
// ]

// USAGE:
//Say you want to know the year, you can do ..
console.log(result2.groups.year); // 2015

//Source: https://github.com/tc39/proposal-regexp-named-groups


4.2 Using Named groups inside regex itself

\k <group name> 형식을 사용하여 regex 자체 내에서 그룹을 다시 참조할 수 있습니다. 다음 예에서는 작동 방식을 보여 줍니다.

let sameWords = /(?<fruit>apple|orange)==\k<fruit>/u;

sameWords.test("apple==apple");   // true
sameWords.test("orange==orange"); // true
sameWords.test("apple==orange");  // false


4.3 Using named groups in String.prototype.replace

이제 명명된 그룹 기능이 String의 replace 인스턴스(instance) 메서드에 복사됩니다. 그래서 우리는 문자열 안에서 단어를 쉽게 바꿀 수 있습니다.

예를 들어 “firstName, lastName”을 “lastName, firstName”로 변경합니다.

let re = /(?<firstName>[A-Za-z]+) (?<lastName>[A-Za-z]+$)/u;

"Raja Rao".replace(re, "$<lastName>, $<firstName>");  //"Rao, Raja"


5. Rest properties for Objects

나머지 연산자 ...는 추출되지 않은 객체 속성을 추출할 수 있게 해줍니다.

5.1 You can use rest to help extract only properties you want

나머지를 사용하여 원하는 속성만 추출할 수 있습니다.

let { firstName, age, ...remaining } = {
	firstName: "john",
	lastName: "smith",
	age: 20,
	height: "5.10",
	race: "martian"
};

firstName; // john
lastName; // 20
remaining; // { lastName: "smith", age: 20, height: "5.10", race: "martian" }


5.2 Even better, you can remove unwanted items! 🔥🔥

게다가 원하지 않는 아이템을 제거할 수 있습니다!

let { SSN, ...cleanObj } = {
	firstName: "john",
	lastName: "smith",
	SSN: "123-45-6789"
	race: "martian"
};

cleanObj; // { firstName: 'john', lastName : 'smith', race: 'martian' }


6. Spread properties for Objects

전개 연산자는 나머지 연산자와 똑같이 보이지만, 차이점은 전개 연산자로 새로운 객체를 만들 수 있습니다.

팁: 전개 연산자는 등호 기호의 오른쪽에 사용됩니다. 나머지는 등호 표지의 왼쪽에 사용된다.

const person = { fName: "john", age: 20 };
const account = { name: "bofa", amount: "$1000" };

const personAndAccount = { ...person, ...account };
personAndAccount; // { fNae: 'john', age: 20, name: 'bofa', amout: '$1000' }


7. RegExp Lookbehind Assertions

이는 RegEx에 대한 향상된 기능으로 일부 문자열이 다른 문자열 바로 직전에 존재하는지 확인할 수 있습니다.

이제 그룹 기능(?<=...)을 사용하여 앞에 해당하는 패턴이 존재하는 지 확인할 수 있습니다.

더 나아가, 당신은 (?<!...)를 사용하여 뒤에 해당하는 패턴이 존재하지 않는지 확인할 수 있습니다. 본질적으로 이것은 검증이 통과하는 한 일치할 것입니.

Positive Assertion: # 기호가 단어 winning 전에 존재하는지 확인하고, regex가 “winning” 문자열만 반환하기를 원한다고 가정해 보겠습니다.

/(?<=#).*/.test("winning") // false
/(?<=#).*/.test("#winning") // true

// BEFORE
"#winning".match(/#.*/)[0]; // "#winning"

// AFTER ECMAScript 2018
"#winning".match(/(?<=#).*/)[0]; // "winning"

Negative Assertion: 표시가 있고 $ 표시가 없는 숫자를 추출하기를 원한다고 가정해 보겠습니다.

// 숫자 3.00은 $ 문자가 직전에 있기 때문에 일치하지 않는다.
"A gallon of milk i $3.00".match(/(?<!\$)\d+\.?\d+/); // null 

// 숫자 2.43은 $ 문자가 직전에 있지 않기 때문에 일치한다.
// 일치하지만 이것은 € 문자를 포함하지 않는다.
"A gallon of milk i €3.00".match(/(?<!\$)\d+\.?\d+/)[0]; // 2.43


8. RegExp Unicode Property Escapes

다양한 유니코드 캐릭터와 어울리는 RegEx를 쓰는 것은 쉽지 않았습니다. \w , \W , \d 등과 같은 것들은 영어 문자와 숫자만 일치합니다. 하지만 힌두어, 그리스어 등 다른 언어의 숫자는 어떨까요?

거기서에서 유니코드 속성 탈출이 시작됩니다. 유니코드는 각 기호(문자)에 대한 메타데이터 속성을 추가하고 이를 사용하여 다양한 기호를 그룹화하거나 특성화합니다. 예를 들어 유니코드 데이터베이스는 Devanagari 값이 있는 Script라는 속성과 Devanagari 값이 동일한 Script_Extensions라는 다른 속성의 모든 힌디어 문자(हिन्दी)를 그룹화합니다. 그래서 우리는 Script=Devanagari를 검색하고 모든 힌두어를 얻을 수 있다.

Devanagari는 마라티, 힌두어, 산스크리트어 등과 같은 다양한 인도 언어들에 사용될 수 있다.

ECMAScript 2018부터 \p를 사용하여 {Script=Devanagari}과 함께 문자를 이스케이프할 수 있습니다. 즉, RegEx의 \p{Script=Devanagari}를 사용하여 모든 Devanagari 문자와 일치시킬 수 있습니다.

//The following matches multiple hindi character
/^\p{Script=Devanagari}+$/u.test('हिन्दी'); //true  
//PS:there are 3 hindi characters h

마찬가지로, Unicode 데이터베이스는 Script_Extensions(및 Script ) 속성의 모든 그리스 문자를 Greek 값으로 그룹화합니다. 따라서 Script_Extensions=Greek 또는 Script=Greek 을 사용하여 모든 그리스 문자를 검색할 수 있습니다.

즉, RegEx의 \p{Script=Greek}를 사용하여 모든 그리스 문자와 일치시킬 수 있습니다.

/\p{Script_Extensions=Greek}/u.test('π'); // true

또한 유니코드 데이터베이스는 불리언 속성인 Emoji, Emoji_Component, Emoji_presentation, Emoji_Modifier에 다양한 유형의 Emo_Modifiertrue 값으로 저장합니다. 그래서 단순히 Emoji를 선택하여 모든 에모지들을 검색할 수 있습니다.

즉, 다양한 종류의 에모지(Emojis)를 일치시키기 위해 \p{Emoji}, \Emoji_Modifier 등을 사용할 수 있습니다.

다음 예에서는 이를 명확하게 설명합니다.

//The following matches an Emoji character
/\p{Emoji}/u.test('❤️'); //true

//The following fails because yellow emojis don't need/have Emoji_Modifier!
/\p{Emoji}\p{Emoji_Modifier}/u.test('✌️'); //false

//The following matches an emoji character\p{Emoji} followed by \p{Emoji_Modifier}
/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true

//Explaination:
//기본적으로 승리의 에모지는 노란색이다.
//동일한 에모지의 갈색, 검은색 또는 다른 변형을 사용할 경우, 
//그것들은 원래 에모지의 변형으로 간주되고 두 개의 유니코드 문자를 사용하여 표현됩니다.
//하나는 원래 이모지로, 그 다음에는 다른 유니코드 캐릭터로 색깔로 나온다.
//
//아래 예에서, 비록 우리가 단 하나의 갈색 승리의 에모지만을 볼 수 있지만
//그것은 실제로 두 개의 유니코드 문자를 사용합니다. 
//하나는 에모지이고 다른 하나는 갈색입니다..
//
//유니코드 데이터베이스에서 이러한 색상은 Emoji_Modifier 속성을 가집니다.
//그러므로 \p{Emoji}와 \p{Emoji_Modifier}를 모두 사용하여 올바르게
//갈색 에모지를 완전히 일치시킵니다.
/\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true

마지막으로, 소문자 p(\p) 대신 대문자 “P”(\P) 이스케이프 문자를 사용하여 일치하는 내용을 부정할 수 있습니다.

References:

ECMAScript 2018 Proposal https://mathiasbynens.be/notes/es-unicode-property-escapes


9. Promise.prototype.finally()

finally()는 Promise에 추가된 새로운 인스턴스 메소드입니다. 주요 아이디어는 문제를 resolve하거나 reject한 후 콜백을 실행하여 문제를 해결하는 것입니다. finally 콜백은 아무런 매개변수 없이 호출되며 어떤 경우에도 항상 실행됩니다.

다양한 사례를 살펴보겠습니다.

// Resolve case

let started = true;

let myPromise = new Promise(function(resolve, reject){
    resolve("all good");
})
    .then(val => {
        console.log(val); // "all good"
    })
    .catch(e => {
        console.log(e);
    })
    .finally(() => {
        console.log("This function is always executed!");

        started = false;
    });
// Reject case
let started = true;

let myPromise = new Promise(function(resolve, reject){
    reject("reject apple");
})
.then(val => {
    console.log(val); // "reject apple"
})
.catch(e => {
    console.log(e);
})
.finally(() => {
    // 아무런 값이 전달되지 않는다
    console.log("This function is always executed!");

    started = false;
});
// Error case1
let started = true;

let myPromise = new Promise(function(resolve, reject){
    throw new Error("Error");
})
.then(val => {
    console.log(val);
})
.catch(e => {
    console.log(e); // 에러가 발생했으므로 이 부분이 실행
})
.finally(() => {
    // 아무런 값이 전달되지 않는다
    console.log("This function is always executed!");

    started = false;
});
// Error case2
let started = true;

let myPromise = new Promise(function(resolve, reject){
    throw new Error("Something happed");
})
.then(val => {
    console.log(val);
})
.catch(e => {
    throw new Error("throw another error");
})
.finally(() => {
    // 아무런 값이 전달되지 않는다
    console.log("This function is always executed!");

    started = false;
});


10. Asynchronous Iteration

이것은 매우 유용한 기능입니다. 기본적으로 쉽게 비동기 코드의 루프를 만들 수 있습니다!

이 기능은 promise(또는 promise 배열)을 반복하여 반환하는 비동기 기능을 호출할 수 있는 새로운 “for-await-of” 루프를 추가합니다. 멋진 것은 다음 루프를 수행하기 전에 각 promise가 resolve될 때까지 기다린다는 것입니다.

const promises = [
    new Promise(resolve => resolve(1)),
    new Promise(resolve => resolve(2)),
    new Promise(resolve => resolve(3)),
];

//BEFORE:
async function test1() {
    for (const obj of promises) {
        console.log(obj); // Logs 3 promise objects
    }
}

//AFTER:
async function test1() {
    for await (const obj of promises) {
        console.log(obj); //Logs 1, 2, 3
    }
}

test1(); // promise, promise, promise
test2(); // 1, 2, 3 ...prints values

Reference