[Javascript] ES13 클래스의 스태틱 필드와 메서드, 스테틱 블록의 기초

ES13의 새로운 지시자인 static은 스테틱(정적) 필드(변수, 속성)와 스테틱 메서드를 클래스에 구현해 줍니다.

스테틱으로 선언한 클래스 필드와 메서드는 클래스 객체를 동적으로 생성하지 않아도 접근할 수 있고, 스테틱 메서드로 스테틱 필드의 값을 읽고 쓸 수 있습니다.

스테틱 메서드와 스테틱 필드를 포함해서 선언한 클래스는 전역으로 컨텍스트가 선언되어 유지됩니다. 따라서 다른 자바스크립트 정적 객체의 함수 호출과 마찬가지로 

1. 스테틱 필드와 스테틱 메서드 선언

스테틱 필드와 스테틱 메서드는 일반 필드, 메서드 앞에 "static"이라는 선언자를 추가해서 선언합니다.

스테틱으로 선언된 필드와 메서드는 클래스의 일반 필드, 메서드와는 구분을 해서 사용하며, 상호 간의 접근 방법도 다릅니다. 클래스의 스테틱 필드와 메서드 선언은 다음처럼 합니다.

스테틱 메서드에서 스테틱 필드에 접근할 때는 현재 콘텍스트를 가리키는 this 지시자로 접근할 수 있습니다.

class Member {
    static age = 0;  
    static getAge() {
      return this.age;
    }  
}

console.log(Member.getAge()) // 0
console.log(Member.age) // 0

필드, 메서드 이름 앞에 붙이면 "#"은 프라이빗 필드, 메서드가 됩니다. 직접 접근할 수 없고 퍼블릭 메서드를 통해서만 접근할 수 있습니다. 프라이빗 지시자가 붙으면 해당 필드 접근은 "#필드명"으로 해야 합니다.

스테틱 필드의 값은 클래스 객체를 생성하지 않아도 초기화된 값이 전역 콘텍스트로 유지됩니다.

프라이빗 스테틱 필드도 일반 프라이빗 필드와 동일하게 직접 접근하면 에러가 발생합니다.

class Member {
    static #age = 0;  
    static getAge() {
      return this.#age;
    }  
}

console.log(Member.getAge()) // 0
console.log(Member.#age) // Uncaught SyntaxError: Private field '#age' must be declared in an enclosing class

2. 스테틱 필드와 스테틱 메서드 접근

클래스의 특별한 메서드인 생성자 메서드(constructor())는 클래스 객체가 생성될 때 객체 초기화 단계에서 한 번 실행됩니다. 동적 생성 클래스 객체가 생성될 때만 호출되는 특별한 메서드이기 때문에 스테틱 필드와 스테틱 메서드에 접근할 수 없습니다.

클래스 생성자 메서드뿐만 아니라 일반 메서드는 실행 콘텍스트가 달라 스테틱 필드와 스테틱 메서드에 접근할 수 없습니다. 

다음처럼 클래스 생성자를 선언하면 에러가 발생합니다.

class Member {
    static age = 0;  
    static getAge() {
      return this.age;
    }  
    setAge() {
        this.age=7;
    }  
    constructor() {
      this.increaseAge();
    }  
    static increaseAge() {
      this.age++;
    }
}

let user1 = new Member() // Uncaught TypeError: this.increaseAge is not a function

생성자 메서드나 일반 메서드에서 스테틱 필드, 스테틱 메서드에 접근하려면 다음과 같이 생성자 객체(constructor)를 통해 접근해야 합니다.

class Member {
    static age = 0;  
    static getAge() {
      return this.age;
    }  
    setAge() {
        this.age=7;
    }  
    constructor() {
      this.constructor.increaseAge(); // 현재 컨텍스트의 constructor 객체를 통해 스테틱 메서드 접근
    }  
    static increaseAge() {
      this.age++;
    }
}

또는 다음처럼 클래스 이름으로 접근을 할 수도 있습니다.

일반 메서드 또한 this.constructor 객체, 또는 클래스명 Member로 스테텍 필드와 메서드에 접근할 수 있습니다.

class Member {
    static age = 0;  
    static getAge() {
      return this.age;
    }  
    setAge() {
        this.constructor.age=7;
    }  
    constructor() {
      Member.increaseAge(); // 클래스 명으로 스테틱 메서드 접근
    }  
    static increaseAge() {
      this.age++;
    }
}

let user1 = new Member()
console.log(Member.age) // 1
user1.setAge() // 일반 메서드로 스테텍 필드 값 갱신
console.log(Member.getAge()) // 7

3. 스테틱 블록

클래스의 멤버 필드를 초기화하는 것과 마찬가지로 스테텍 필드도 클래스 선언 시점에 초기화를 할 수 있습니다.

클래스 내부 일반 코드 블록 앞에 static 선언자를 붙이면 스테텍 콘텍스트에 접근할 수 있게 되면서 this 지시자로 스테틱 필드를 초기화할 수 있습니다.

class Car {
    static defaultEngine = 'gasoline';
}

class Suv extends Car {
    static engines = [];  
    static {
        this.engines.push(super.defaultEngine, 'diesel');
    }  
    static {
        this.engines.push('hybrid');
    }
    getEngines(){
        return this.engines
    }
}

console.log(Suv.engines); // [ 'gasoline', 'diesel', 'hybrid' ]

선언한 클래스를 객체를 생성해서 엔진 종류를 보기 위해 getEngines() 메서드를 호출합니다.

클래스 명으로 정적 필드인 engines에 접근하면 스테틱 블록에서 초기화를 해서 추가한 배열 값들을 확인할 수 있습니다.

let myCar = new Suv()
console.log(myCar.getEngines()) // undefined

!주의해야 합니다. 이 클래스에는 문제가 있는 일반 메서드가 선언되어 있습니다.

engines 필드가 스테틱이기 때문에 일반 메서드인 getEngines() 안에서는 this 지시자로 접근할 수 없습니다.

this.constructor.engines 또는 Suv.engines로 접근해야 합니다. 따라서 다음과 같이 메서드를 수정해야 합니다.

getEngines(){
    return this.constructor.engines
}