ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] ES6 이후 - (2) ES2019, ES2020, ES2021
    Front-End(Web)/Javascript 2022. 2. 9. 13:15
    반응형

    이전 포스팅에서, 최근 Javascript의 문법이 격변했던 ES6(ES2015)에 대해 정리한 적이 있다.

    (포스팅 링크 : https://abangpa1ace.tistory.com/146?category=910462)

     

    [Javascript] ES6(ES2015)

    🧐 서론 드디어, 내가 이전부터 포스팅하고 싶었던 Javascript의 ES6 문법이다! 나는 2020년에 Javascript를 처음 공부했고, 오히려 ES6 문법을 당연하다는 듯이 사용하게 된 뉴비 중 한명이다. 😚😚😚

    abangpa1ace.tistory.com

     

    ES6 버전업 된 2015년 이후, Javascript는 매년 TC39에 의해 버전업이 진행되고 있으며, ES2018(ES9) 버전까지 포스팅했었다.

    (포스트 링크 : https://abangpa1ace.tistory.com/230?category=910462 )

     

    [Javascript] ES6 이후 - (1) ES2016, ES2017, ES2018

    이전 포스팅에서, 최근 Javascript의 문법이 격변했던 ES6(ES2015)에 대해 정리한 적이 있다. (포스팅 링크 : https://abangpa1ace.tistory.com/146?category=910462) [Javascript] ES6(ES2015) 🧐 서론 드디어,..

    abangpa1ace.tistory.com

     

    이번 포스팅은 위에 이은 2탄으로, ES2019, ES2020, 그리고 가장 최근(작년 6월) 업데이트된 ES2021의 내용까지 정리해보겠다!


    📒 ES6(ES2015) 그 이후?

     

    ES는 ECMA라는 정보통신 기술 표준을 제정하는 단체에서 지정한 Script 프로그래밍 언어라는 뜻이며, 이것의 6버전이 2015년에 업데이트된 것이다.

     

    하지만, Javascript가 초기에 급하게 설계된 언어인만큼 보완되어야 할 부분이 많았으며, ES6 이후로는 매년 업데이트를 진행하고 있다.

    (그래서, ES7, 8, 9.. 버전 카운팅이 모호하여, ES2016, 2017.. 연도 카운팅으로 버저닝하기도 한다.)

     

    현재는 ECMA의 TC39에서 매년 6월 ECMAScript의 새 버전을 출시하고 있다.

    * TC39(Technical Committee 39) : JS의 전신인 Mocha를 개발한 Brendan Eich가 속한 팀. ECMA-262 제정을 담당한다.

     


     

    - ECMAScript10 (2019)

     

    이번 버전도 큰 변경사항은 없었다. 편의성에 관련된 몇몇 기능들만 추가되었다.

     

    1) Array.prototype.flat()

     

    2-depth 이상의 배열을 1차원 배열로 펼쳐주는 메서드이다.

    인자는 depth레벨 1개를 받으며, 기본값은 1이다. depth에 Infinity를 넣어주면, 모든 depth를 풀 수 있다.

    const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
    const arr1 = arr.flat();		// [ 1, 2, 3, 4, 5, 6, [ 7, 8, 9 ] ]
    const arr2 = arr.flat(2);		// [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
    const arr3 = arr.flat(Infinity); 	// [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

     

     

    2) Array.prototype.flatMap()

     

    flatMap()은 flat()과 map() 기능을 합친 메서드이다. 배열을 mapping함과 동시에, 내부의 배열들은 1차원으로 풀어준다.

    const arr = ["harry", "iu"];
    
    const arrMap = arr.map((elem) => elem.split(""));		
    // [ [ 'h', 'a', 'r', 'r', 'y' ], [ 'i', 'u' ] ]
    
    const arrFlatMap = arr.flatMap((elem) => elem.split(""));	
    // [ 'h', 'a', 'r', 'r', 'y', 'i', 'u' ]

     

     

    3) Object.fromEntries()

     

    이전 ES2017에 출시된 Object.entries() 의 반대동작을 하는 메서드다. 튜플배열을 객체로 반환한다.

    const arr = [
      ["x", "11"],
      ["y", "22"],
      ["z", "33"],
    ];
    const obj = Object.fromEntries(arr);
    // { x: '11', y: '22', z: '33' }

     

     

    4) String.prototype.(trimStart, trimLeft / trimEnd, trimRight)

     

    기존 trim() 메서드는 문자열의 양쪽 공백을 지웠지만, 왼쪽 혹은 오른쪽을 선택적으로 트리밍하는 메서드이다.

    '    string    '.trim(); 	// 'string'
    '    string    '.trimStart(); 	// 'string    '
    '    string    '.trimLeft(); 	// 'string    '
    '    string    '.trimEnd(); 	// '    string'
    '    string    '.trimRight(); 	// '    string'

     

     

    5) Optional Catch Binding

     

    기존 try-catch 문은 error 매개변수를 사용하지 않아도 반드시 작성하여야 했다. 이 부분이 필수가 아니도록 수정되었다.

    // ES10
    try{
      new Error('hello');
    } catch {	// (error) 생략가능
      console.log('에러!!');
    }
    
    // ~ES9
    try{
      new Error('hello');
    } catch(error){
      console.log('error를 쓰지 않는다~!');
    }

     

     

    6) Function.toString()

     

    함수를 문자열로 변환하는 경우, 포함되지 않던 주석을 포함시켰다.

    // ES10
    function /* 사용자 함수 */ myFunction () {}
    
    myFunction.toString();
    // "function /* 사용자 함수 */ myFunction () {}"
    
    
    // ~ES9
    function /* 사용자 함수 */ myFunction () {}
    
    myFunction.toString();
    // "function myFunction() {}"

     

     

    7) Symbol.description

     

    Symbol 자료형의 읽기전용 속성인 description이 추가되었다. 명명의 목적이 있으며, 디버깅을 수월히 하기 위함이다.

    const mySymbol = Symbol("my symbol");
    console.log(mySymbol.description); // my symbol

     

     

    8) Well-formed JSON.stringify()

     

    JSON 내에서 유니코드(UTF-16)를 해석하기 위해 기능이 개선되었다. 단일 코드유닛이 아닌, JSON escape sequence로 표현할것을 권장한다.

    // Non-BMP characters still serialize to surrogate pairs.
    JSON.stringify('𝌆')
    // → '"𝌆"'
    JSON.stringify('\uD834\uDF06')
    // → '"𝌆"'
    
    // Unpaired surrogate code units will serialize to escape sequences.
    JSON.stringify('\uDF06\uD834')
    // → '"\\udf06\\ud834"'
    JSON.stringify('\uDEAD')
    // → '"\\udead"'

     

     

    9) Array.sort() Stability

     

    이전까지 V8엔진 기준 QuickSort (a - b) 가 10개 요소가 넘어갈 경우 안정성이 떨어졌지만, 512 요소까지 안정성이 보강되었다.

     

     

    10) JSON ➢ ECMAScript (JSON Superset)

     

    ECMA-262가 JSON을 슈퍼셋으로 포함했다. 기존에는 ECMA-404에서 명세하였다.

     


     

    - ECMAScript11 (2020)

    ES2020에도 많은 기능들이 추가되었으며, 특히 실무에서 유용한 기능들이 많이 포함되어있다. (Optional Chaining, Nullable 등)

     

    1) BigInt

     

    Javascript에서 숫자형(Number)의 최대값은 (2^53 - 1)로 제한되어있다. 더 큰 값을 저장할 순 있지만, 연산단계에서 오류가 발생한다.

     

    BigInt는 이보다 큰 숫자를 담기 위한 자료형(객체)이며, 정수 리터럴 뒤에 n을 붙이거나 BigInt(숫자) 생성자로 생성할 수 있다.

    계산은 BigInt끼리만 가능하며 소수점은 쓸 수 없다. (단, Number와 비교, 정렬은 가능)

    let bigNum = BigInt(9007199254740991);
    console.log(bigNum); // 9007199254740991n
    bigNum++;
    console.log(bigNum); // 9007199254740992n
    bigNum++;
    console.log(bigNum); // 9007199254740993n
    bigNum++;
    console.log(bigNum); // 9007199254740994n
    console.log(typeof bigNum); // bigint

     

     

    2) String.prototype.matchAll

     

    matchAll() 메서드는 match() 의 복수형으로, 문자열에서 일치하는 검색결과들을 이터레이터로 반환한다.

    const reg = /\w+/g;
    const str = 'Hello Harry and IU';
    const regs = str.matchAll(reg); // Object [RegExp String Iterator] {}
    console.log(Array.from(regs));
    /*
    [
      [ 'Hello', index: 0, input: 'Hello Harry and IU', groups: undefined ],
      [ 'Harry', index: 6, input: 'Hello Harry and IU', groups: undefined ],
      [ 'and', index: 12, input: 'Hello Harry and IU', groups: undefined ],
      [ 'IU', index: 16, input: 'Hello Harry and IU', groups: undefined ]
    ]
    */

     

     

    3) Optional Chaning(선택적 연결 연산자) - ?.

     

    depth가 깊은 객체의 프로퍼티를 조회할 경우, key값을 중첩해서 사용할 때 상위 키값이 존재하지 않으면 error가 발생한다.

    const person1 = {
      name: 'harry',
      age: 20,
    };
    
    const person2 = {
      name: 'IU',
      age: 29,
      job: {
        name: 'singer',
        location: 'Seoul',
      },
    };
    
    console.log(person1.job.name); 
    // Cannot read property 'name' of undefined

     

    이를 탐지해서, 존재하지 않을 경우 undefined를 반환하기 위한 연산자이다. 문법은 [객체].[키값1]?.[키값2] 로 쓸 수 있다.

    이전의 조건부 연산자를 사용할 때 비해 코드가 확연히 짧아진 모습이다.

    // ES11
    console.log(person1.job?.name); // undefined
    console.log(person2.job?.name); // singer
    
    
    // ~ES10
    console.log(person1.job && person1.job.name); // undefined
    console.log(person2.job && person2.job.name); // singer

     

     

    4) Nullish Coalescing Operator(null 통합 연산자) - ??

     

    기존의 데이터 분기를 적용할 때, OR(||) 연산자를 많이 사용하였다. 이는, 0, '', NaN 과 같은 falsy값들이 유효한 경우를 구분할 수 없다.

    const data = {
      name: '',
      age: 20,
    }
    
    const myAge = data.age || 18		// 20
    const myName = data.name || 'default'	// "default"

     

    이를 구분하고, 오직 null과 undefined 인 경우에만 디폴트 값을 할당하기 위해 이 연산자를 사용하는 것이다.

    const data = {
      name: '',
      age: 20,
    }
    
    const myAge = data.age || 18		// 20
    const myName = data.name || 'default'	// ""

     

     

    5) globalThis

     

    어떠한 JS 실행환경에서도 전역객체를 반환한다. 브라우저는 window, node.js는 global라는 전역객체를 각각 사용하다, globalThis로 통일되었다. (기존 전역객체도 사용가능)

    // In a browser
    window == globalThis // true
    
    // In node.js
    global == globalThis // true

     

     

    6) Top Level Await

     

    기존 await는 항상 async function 내부에서만 허용되었지만, 최상위 스코프에서도 await을 사용할 수 있다.

    // ES11
    
    function asyncSleep(ms) {
        return new Promise((resolve) => {
            setTimeout(resolve, ms);
        });
    }
    
    await asyncSleep(1000);
    console.log("Hello, World!");
    
    
    // ~ES10
    function asyncSleep(ms) {
        return new Promise((resolve) => {
            setTimeout(resolve, ms);
        });
    };
    
    async main(){
        await asyncSleep(1000);
        console.log("Hello, World!");
    }
    main();

    * Node.js에서도 v14.8.0부터는 --harmony-top-level-await 플래그가 불필요하다. 단, ES Modules에서만 동작하므로 package.json에 "type": "module"을 설정하거나 .mjs 확장자로 변경해야한다.

     

     

    7) Promise.allSettled()

     

    Promise.all() 메서드가 있었지만, 이는 Promise들 중 하나만 실패해도 catch로 이동한다는 문제점이 있었다.

    그렇기에, allSettled() 메서드는 모든 Promise들을 개별적으로 성공과 실패 여부로 반환해준다.

    const promises = [
      new Promise((res) => setTimeout(res, 1000)),
      new Promise((res) => setTimeout(res, x)),
      new Promise((res) => setTimeout(res, 1000)),
    ];
    Promise.allSettled(promises).then((data) => console.log(data));
    
    /*
    [
      { status: 'fulfilled', value: undefined },
      { status: 'rejected', reson: ReferenceError: x is not defined ... },
      { status: 'fulfilled', value: undefined }
    ]
    */

     

     

    8) Dynamic Import

     

    동적 import() 는 모듈 객체의 네임스페이스를 Promise 객체로 반환한다. 그렇기에 비동기 문법으로 사용한다.(Promise, async/await)

    // option 1
    const helperModule = './helpers.js'
    import(helperModule).then((module) => {
      module.doStuff1()  // Method 1
      module.doStuff2()  // Method 2
    })
    
    // option 2
    (
      async function importCheck () {
        const helperModue = './helpers.js'
        const module = await import(helperModule)
        module.doStuff1()  // Method 1
        module.doStuff2()  // Method 2
      }
    )()

     

     

    9) Module Exports Namespace

     

    import와 같이, export에서도 모듈의 이름을 설정할 수 있다.

    // ES11
    export * as utils from './utils.js'
    
    
    // ~ES10
    import * as utils from './utils.js'
    export { utils }

     

     

    10) Import Meta

     

    import.meta 객체를 통해 모듈의 정보를 확인할 수 있다.

    console.log(import.meta);
    
    // { url: "file:///home/user/my-module.js"; }

     

     

    11) Class 문법 : Private Fields

     

    클래스 내부적으로만 사용되는 변수(메서드)를 기존에는 클로저로 구현했지만, 이젠 #[변수명] 문법을 통해 비공개 필드로 선언할 수 있다.

    // ES11
    
    class Box {
        #value = 0;
    
        getValue() {
            return this.#value;
        }
    
        setValue(value) {
            this.#value = value;
        }
    }
    
    const box = new Box();
    console.log(box.getValue()); // 0
    console.log(box.#value); // error
    // ~ES10(Functional Class)
    
    function Box() {
        let _value = 0;
    
        return new (function Box() {
            this.getValue = function () {
                return _value;
            };
    
            this.setValue = function (value) {
                _value = value;
            };
        })();
    }
    
    const box = new Box();
    console.log(box.getValue()); // 0
    console.log(box._value); // error

     

     

    12) Class 문법 : Static Fields

     

    클래스에서 필드 앞에 static 키워드를 붙이면 해당 필드는 클래스의 소유가 된다. 이는, 클래스를 인스턴스화 하지 않고 바로 사용할 수 있게 된다.

    // ES11
    
    class Smartphone {
      designer(color) {
        this.color = color;
      }
      static create_smartphone(color) {
        return new Smartphone(color);
      }
    }
    const silver = Smartphone.create_smartphone("silver"); // output is: undefined
    // ~ES10
    
    class Smartphone {
      add_color() {
        console.log("Adding Colors");
      }
    }
    const apple = new Smartphone();
    apple.add_color(); // output is: Adding Colors
    
    Smartphone.add_color() // TypeError: Smartphone.add_color is not a function

     


     

    - ECMAScript12 (2021)

    ES2021는 가장 최근에 갱신된 ECMAScript 버전으로, 많은 내용이 포함되어있진 않지만 몇 가지 유용한 기능들이 추가되었다.

     

    1) String.prototype.replaceAll()

     

    이전에는, 부분 문자열 전체를 대체하는 방법으로 정규식의 g Flag를 활용했지만, 이를 replaceAll()로 손쉽게 구현할 수 있게 되었다.

    문법은 replaceAll(targetValue, replaceValue) 2가지 인자를 넘겨준다.

    // ES12
    const fruits = '🍎+🍐+🍓+';
    const fruitsWithBanana = fruits.replaceAll('+', '🍌');
    console.log(fruitsWithBanana); //🍎🍌🍐🍌🍓🍌
    
    
    // ~ES11
    const fruits = '🍎+🍐+🍓+';
    const fruitsWithBanana = fruits.replace(/\+/g, '🍌');
    console.log(fruitsWithBanana); //🍎🍌🍐🍌🍓🍌

     

     

    2) Promise.any()

     

    Promise 이터레이터를 인자로 받아 하나라도 성공한다면 전체를 완료한다. 만약, 모두 실패한다면 모든 실패 이유가 포함된 AggregateError가 발생한다.

     

    ES6에서 등장한 Promise.race() 와 유사한 듯 보이나, Promise.race() 는 Promise 이터레이터 중 하나라도 성공 혹은 실패하면 전체를 완료한다는 차이점이 있다.

    const promise1 = new Promise((resolve) => setTimeout(resolve, 300, 'soso'));
    const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
    const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
    
    Promise.all([promise1, promise2, promise3]).then((values) => {
      console.log(values);
    });
    // Array ["soso", "quick", "slow"]

     

     

    3) WeakRef

     

    약한 참조(Weak References)를 의미한다. 이것의 주요 목적은 캐시 혹은 대형 객체에 대한 매핑을 하여, 메모리에 오랜시간 유지되지 않도록 하기 위해 사용된다.

     

    Javascript 엔진 내에서는 Garbage Collector가 상시로 동작하면서 도달할 수 없는 객체를 메모리에서 제거한다.

    이 때, 객체에 일반 참조가 있다면 G.C는 객체를 제거하지 않지만, 약한 참조가 걸려있다면 메모리에서 제거하게 된다.

    let common = {
      name: 'common',
    };
    
    const commonWeakRef = new WeakRef(common);
    
    const printName = () => {
      console.log(commonWeakRef.deref().name); 
      // deref()약한 참조를 읽는 메서드
    };
    
    common = null;
    
    setInterval(printName, 3000);

    메모리 효율성은 향상될 수 있으나, 객체가 회수된 후 에러가 출력되기도 하고 Javascript 엔진 버전이나 케이스에 따라 다양한 아웃풋이 형성될 수 있어 가능하면 WeakRef의 사용을 지양하고 있다.

     

     

    4) Logical Assignment Operator (논리 할당 연산자)

     

    ES2021에 3가지 논리 연산자가 추가되었다. (연산자에 할당 기능이 더해져 있는 것으로, 기존 +=, -= 연산자들이 이에 해당된다.)

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

     

     

    5) 숫자 구분 기호

     

    긴 자리수의 숫자를 구분하기에 용이해졌다. _(언더바)를 사용하면 되며, 자리수를 구분하는 콤마처럼 사용하면 된다.

    // ES12
    10_000_000_000 // 100억
    
    
    // ~ES11
    10000000000 // 100억

    이로써, ES6 이후 ECMAScript의 업데이트 내용들을 확인해보았다.

     

    이 포스팅 시리즈를 기획하게 된 이유는 크게 2가지가 있었다.

     

    첫 번째는, 사파리(구, 13버전) 디버깅 중 확인한 최신문법 미지원이었다.

    회사 프로젝트를 개발하던 중, 선배가 적용한 Promise.any()가 구버전 사파리 브라우저에서 동작하지 않고 에러가 발생하였다.

    그렇기에, 상대적으로 최신에 나온 문법들은 어떠한 것이 있고, 내가 만약 종종 사용하는 것이라면 최대한 사용을 지양하도록 하기 위함이다.

     

    두 번째는, 최신 문법에 대한 정확한 이해이다.

    나는 작년에 JS개발을 시작했기에, 당연하게 쓰던 문법들이 생각보다 출시한지 얼마 안됬다는 점에서 놀랐다.

    (Object.values, String.replaceAll, 동적 import 등)

    또한, nullable 연산자(??)의 경우엔 정확한 의미를 몰랐으며, 외에도 가장 최근에 나온 문법들은 어떤 것이 더 있는지 궁금해서 공부하고자 하는 목적이 가장 컸었다.

     

    이전 포스팅에서 언급했듯이, ECMA는 스크립트 버전을 매 해마다 갱신하고 있다. 최신 문법을 공부하는 목적에서, 이를 매년 포스팅하는 버릇을 들여야겠다!!

     

    📎 출처

    - [ES2019] selvaganesh93's blog : https://selvaganesh93.medium.com/javascript-whats-new-in-ecmascript-2019-es2019-es10-35210c6e7f4b

    - [ES2020] AeroCode 님의 블로그 : https://aerocode.net/383?category=822607  

    https://javascript.plainenglish.io/some-of-useful-javascript-features-from-es2016-to-es2021-4c08a9a80a19

    - [ES2020] john015 님의 블로그 : https://john015.netlify.app/what-is-new-in-es-11

    - [ES2021] Harry's diary 블로그 : https://haesoo9410.tistory.com/32

    - [ES2021] CHANYEONG 님의 블로그 : https://chanyeong.com/blog/post/29  

    - [ES2016-2020] junhobaik 님의 블로그 : https://junhobaik.github.io/es2016-es2020/ 

    반응형
Designed by Tistory.