카테고리 없음

ECMA 2022를 알아보자

qloz 2022. 7. 12. 17:04
728x90

ES (EcmaScript)란?

  • 표준을 위해 자바스크립트를 ECMA(European Computer Manufactures Association)라는 정보와 통신시스템의 비영리 표준 기구에 제출하였고, 표준에 대한 작업을 ECMA-26란 이름으로 1996년 11월에 시작해 1997년 6월에 채택되었다.
  • 즉, ES란 ECMA Script의 약자로 스크립트 언어가 준수해야 하는 규칙, 세부 사항 및 지침을 제공한다.

ES2022를 알아보자~~

객체지향을 위한 클래스의 기능들이 추가되었다.

TS나 바벨을 기존에 사용했던 개발자들이라면 이미 쓰고있던 기능이겠지만, 정식스펙으로 나왔다.

 

1. Class Fields

  • Class Fields생성자를 통해 필드를 정의할 수 있었다.
  • 일반적으로 클래스 메서드 외부에서 액세스하면안되는 필드에는 밑줄이 붙었지만, 클래스 사용하는 사람들을 막지 못하였다.
  • Class Public Instance Fields & Private Instance Fields
    class ColorButton extends HTMLElement {
      
      constructor() {
        this.color = "red"
        this._clicked = false
      }
    }
    
    const button = new ColorButton()
    // Public fields can be accessed and changed by anyone
    button.color = "blue" 
    
    // Curse your sudden but inevitable betrayal 
    console.log(button._clicked) // Prints: false, can be accessed from the instance
    button._clicked = true // Doesn't throw an error, can be read from the instance
    생성자내에 정의하는 대신, 클래스의 최상단 레벨에 정의할 수 있다!즉, constructor이 아닌, 외부에서 선언이 가능하다.
    매개변수를 받아서 사용하려면 constructor함수를 사용해야한다.

 

 class ColorButton extends HTMLElement {
   // All fields are public by default
   color = "red"

   // Private fields start with a #, can only be changed from inside the class
   #clicked = false
 }

 const button = new ColorButton()
 // Public fields can be accessed and changed by anyone
 button.color = "blue"

 // SyntaxError here 
 console.log(button.#clicked) // Cannot be read from outside
 button.#clicked = true // Cannot be assigned a value from outside

private필드를 조금 더 안전하게 숨길 수 있다.

밑줄을 붙이는 기존의 방식과 달리 필드 이름 앞에 #을 붙여 외부의 엑세스를 방지할 수 있다.

 

class ColorButton extends HTMLElement {
  // All fields are public by default
  color = "red"

  // Private fields start with a #, can only be changed from inside the class
  #clicked = false
}

const button = new ColorButton()
// Public fields can be accessed and changed by anyone
button.color = "blue"

// SyntaxError here 
console.log(button.#clicked) // Cannot be read from outside
button.#clicked = true // Cannot be assigned a value from outside
  • Private instance methods and accessors메소드나 접근자 앞에 #을 붙일 수 있다.
  • 클래스의 몇몇 메소드나 변수는 클래스 내부적으로 기존에 의도했던 기능들을 수행해야 하는 중요도를 가지면서 외부에서 접근할수 없어야 한다.
  • 메소드나 접근자 앞에 #을 붙일 수 있다.
class Banner extends HTMLElement {
  // Private variable that cannot be reached directly from outside, but can be modified by the methods inside:

  #slogan = "Hello there!"
  #counter = 0

  // private getters and setters (accessors):

  get #slogan() {return #slogan.toUpperCase()}
  set #slogan(text) {this.#slogan = text.trim()}

  get #counter() {return #counter}
  set #counter(value) {this.#counter = value}

  constructor() {
    super();
    this.onmouseover = this.#mouseover.bind(this);
  }

  // private method:
  #mouseover() {
    this.#counter = this.#counter++;
    this.#slogan = `Hello there! You've been here ${this.#counter} times.`
  }
}
  • Static class fields and private static methods이 필드와 메소드들이 클래스 내에서만 액세스할 수 있도록 허용할 수 있다.
  • 정적 필드나 메소드는프로토타입 내에서만 존재하도록 하는 데 있어 유욯다지만, 주어진 클래스의 모든 인스턴스에 대해서는 그렇지 않다.
  • 이 필드와 메소드들이 클래스 내에서만 액세스할 수 있도록 허용할 수 있다.
class Circle {}
Circle.PI = 3.14

위 코드의 경우 static class fields를 정의할 수 있으므로 아래코드처럼 사용 가능하다.

class Circle {
  static PI = 3.14
}

클래스 필드, 메소드에서 했던 것처럼 #을 붙여 private하게 만들 수 있다.

오직 클래스 내부에서만 액세스하 수 있도록 외부에서의 액세스를 방지한다.

class Circle {
  static #PI = 3.14

  static #calculateArea(radius) {
    return #PI * radius * radius
  }

  static calculateProperties(radius) {
    return {
      radius: radius,
      area: #calculateArea(radius)
    }
  }

}

// Public static method, outputs {radius: 10, area: 314}
console.log(Circle.calculateProperties(10))

// SyntaxError - Private static field
console.log(Circle.PI)

// SyntaxError - Private static method
console.log(Circle.calculateArea(5))

 

2. Ergonomic brand checks for Private Fields (private 필드에 대한 인체공학적 체크)

public 필드에 대해, 클래스의 존재하지 않는 필드에 접근을 시도하면 undefined가 반환된다.

반면에 private필드는 undefined대신 예외를 발생시킨다.

 

이 방법의 큰 단점은? 존재하는 필드에 대한 잘못된 접근자로 인한 이유처럼 다른 이유로 인해 예외가 발생할 수도 있다.

 

위 단점을 해결하기 위해 in 키워드를 사용해 private 속성/ 메소드를 체크할 수 있어야한다.

class VeryPrivate {
  constructor() {
    super()
  }

  #variable
  #method() {}
  get #getter() {}
  set #setter(text) {
    this.#variable = text
  }

  static isPrivate(obj) {
    return (
      #variable in obj && #method in obj && #getter in obj && #setter in obj
    )
  }
}

3. RegExp Match Indices

정규식을 사용해 문자열의 패턴을 찾는 방법!

Recexp.exec과 String.matchAll 모두 match된 리스트를 결과로 보여준다.

전자의 경우, 결과를 하나하나 반환하기 때문에, null이 반환될 때까지 여러번 실행

후자는 모든 match에 대해 순회할 수 있도록 iterator를 반환한다.

이러한 결과에는 일치하는 문자의 전체 문자열과 괄호화된 하위 문자열, 입력 문자열 및 일치 항목의 0 기반 인덱스가 모두 포함된다.

 

const str = 'Ingredients: cocoa powder, cocoa butter, other stuff'
const regex = /(cocoa) ([a-z]+)/g
const matches = [...str.matchAll(regex)]

// 0: "cocoa powder", 1: "cocoa", 2: "powder"
// index: 13
// input: "Ingredients: cocoa powder, cocoa butter, other stuff"
console.log(matches[0])

// 0: "cocoa butter", 1: "cocoa", 2: "butter"
// index: 27
// input: "Ingredients: cocoa powder, cocoa butter, other stuff"
console.log(matches[1])

4. Top-level await

await은 async 함수 내에서만 사용할 수 있었지만, 모듈 최상단에서 await을 쓸 수 없었다.

ECMA 2022부터는 await은 모듈 최상단에서 사용할 수 있으며, import, fallback 등을 만들 때 매우 유용하다.

await Promise.resolve(console.log('테스트'))

awaited promise가 해결되기까지, 현재 모듈과 현재 모듈을 Import하는 상위 모듈의 실행은 지원하지 않았지만, 형제 모듈은 동일한 순서로 실행될 수 있다.

5. at

배열의 가장 마지막 요소를 선택할 때 원래는 아래 코드처럼 작성해야했다.

const arr = [1, 2, 3, 4, 5];
arr[arr.length - 1]; // 5

ECMA2022는 at 메소드를 추가하여 아래와 같은 코드처럼 작성할 수 있다

const arr = [1, 2, 3, 4, 5];
arr[arr.length - 1]; // 5

 

6. error cause

에러 체이닝을 위해 도입된 속성이다.

우리는 api를 호출하면서 try catch문을 이용하여 error를 받았다.

하지만, 서버측에서 보낸 에러는 어디에서도 나와있지 않다.

서버측 에러를확인하기 위해 cause를 사용하여 에러를 확인할 수 있다.

 

async function doJob() {
  const rawResource = await fetch('//domain/resource-a')
    .catch(err => {
      throw new Error('Download raw resource failed', { cause: err });
    });
  const jobResult = doComputationalHeavyJob(rawResource);
  await fetch('//domain/upload', { method: 'POST', body: jobResult })
    .catch(err => {
      throw new Error('Upload job result failed', { cause: err });
    });
}

try {
  await doJob();
} catch (e) {
  console.log(e);
  console.log('Caused by', e.cause);
}

7. hasOwn()

  • 객체의 특정 속성이 프로토타입을 거치지 않은 객체 그 자신이 소유한 속성인지를 반환하는 메소드
  • Object.prototype.hasOwnProperty() 를 대체한다
    • 프로토타입이 없거나 재정의된 객체에서는 사용할 수 없기 때문에!
const proto = {
	protoProp: '프로토타입 속성',
};
const object = {
	__proto__: proto,
	objProp: '객체속성',
}

// in 연산자는 객체의 속성에 해당 값이 있는지를 반환
console.log('protoProp' in object); // true

// hasOwn 메소드는 프로토타입 속성이 아닌 **객체 고유의 속성**인지를 반환한다.
console.log(Object.hasown(object, 'protoProp')) // false
console.log(Object.hasown(proto, 'protoProp')) // true

 

728x90