티스토리 뷰

자바스크립트 클래스 필드

원래 자바스크립트의 클래스에서 인스턴스 프로퍼티는 constructor 내부에, 프로토타입 메서드 또는 정적 메서드는 클래스 몸체에 선언해야 한다.

class Person {
  constructor(name) {
    // name은 인스턴스 프로퍼티
    this.name = name;
  }
  // printName은 프로토타입 메서드
  printName() {
    console.log(this.name);
  }
}

참고로 이건 이 포스팅 내용과는 상관 없지만 위 코드는 아래 코드랑 거의 비슷하다. 생성자 함수 방식에서는 프로토타입 메서드를 만들려면 프로토타입 프로퍼티에 접근해서 메서드를 추가해야 하지만 클래스에서는 그냥 정의하면 프로토타입 메서드가 된다.

const Person = (function () {
     function Person(name) {
      this.name = name;
    }

    Person.prototype.printName = function() { console.log(this.name); } 
})();

어쨌든, 이제 자바스크립트 클래스 몸체에 바로 인스턴스 프로퍼티를 선언할수 있는 새로운 사양이 TC39 프로세스의 stage3에 올라와 있다. (현재 2021년 3월 기준)

이 사양 이름은 Class field declarations 이다. 이미 최신 브라우저와 최신 노드에서는 이 사양을 반영하고 있다. 이 것이 반영되면 클래스 몸체에도 아래처럼 인스턴스 프로퍼티를 정의할 수 있게 된다.

class Person {
  name = 'Leo';
}

const person = new Person();
person.name; // 'Leo'

주의해야할 점은 클래스 필드를 정의할 때에는 this 바인딩을 하지 말아야 한다는 것이다. this는 constructor 내부나 메서드 내에서만 유효하다. constructor 내부에서 this를 쓰는 이유는 생성자의 패러미터 이름과 동일하여 클래스 필드임을 명확하게 하기 위해서 사용한다고 생각하면 된다. 그러므로 constructor 내부가 아닌 몸체에서 클래스 필드를 정의할 때에는 this를 쓰지 않아도 된다고 생각하면 기억하기 편할 듯하다. 하지만 클래스 필드를 참조할 때에는 반드시 this를 붙여서 해줘야 한다.

클래스 필드를 생성할 때 외부의 초기값으로 필드를 초기화해야 한다면, 어차피 생성자 내에서 클래스 필드를 참조해서 할당해야 하므로 별도로 클래스 필드를 선언할 필요가 없다. 그러므로 이제 인스턴스 프로퍼티를 생성할 때 2가지 방법이 있다고 생각하면 된다.

  • 외부에서 값을 받아 초기화해야 될 때 -> 생성자 내에서 정의
  • 외부에서 값을 받아 초기화할 필요가 없을 때 -> 생성자 내에서 정의 또는 클래스 필드로 정의

클래스 필드와 이벤트 핸들러 this 바인딩

자바스크립트에서 함수는 일급 객체이므로 클래스 필드에 함수도 할당할 수 있다. 그리고 이걸 화살표 함수로 정의할 수도 있다. 이렇게 클래스 필드에 메서드를 정의하면 이는 프로토타입 메서드가 아니라 인스턴스 메서드가 된다. 클래스 필드에 정의되는 프로퍼티는 모두 인스턴스 프로퍼티이기 때문이다.

class Person {
  name = 'Leo';

    // 기존 프로토타입 메서드 
    // getName() {
    //  return this.name; 
    // }

    // 화살표 함수로 정의한 인스턴스 메서드
    getName = () => this.name; 
}

그리고 이제 화살표 함수로 메서드를 정의할 수 있게 되었으니 클래스의 이벤트 핸들러에서 this를 바인딩하는 방법도 하나가 더 추가된 셈이다.

원래 이벤트 핸들러 프로퍼티 방식은 동일한 이벤트는 한 번밖에 등록하지 못하지만 비교 편의를 위해 onclick 이벤트 핸들러를 두 개 정의했다.

class App {
  constructor() {
    this.$button = document.querySelector('.btn');
    this.count = 0; 

    this.$button.onclick = this.increaseMethod.bind(this);
    this.$button.onclick = this.increaseArrowFunc;
  }

  increaseMethod() {
    // 원래 이벤트 핸들러 내부의 this는 이벤트가 바인딩된 DOM 요소를 가리킨다.
    // 여기서 this가 생성될 인스턴스를 가리키게 하려면 위에서 바인딩을 해줘야 한다. 
    this.$button.textContext = ++this.count;
  }

  // 화살표 함수는 자신의 this가 없고 상위 스코프의 this를 참조하므로,
  // 화살표 함수 내부의 this는 생성될 인스턴스를 가리킨다. 
  increaseArrowFunc = () => this.$button.textContext = ++this.count;
}

원래 이벤트 핸들러 내부의 this는 이벤트가 바인딩된 DOM 요소(currentTarget)를 가리킨다. (정확히 말하자면 addEventHandler와 이벤트 핸들러 프로퍼티 방식이 그렇다. 이벤트 핸들러 어트리뷰트 방식은 다르다.)

그러므로 increaseMethod 내부의 this는 사실 this.$button 을 가리키므로, this.$button 코드는 실제로는 this.$button.$button을 가리키게 되어 에러가 난다. 이런 문제를 방지하기 위해 이벤트 핸들러를 바인딩할 때에 bind 함수를 사용해서 적절한 객체를 넣어줘야 한다. bind 함수의 아규먼트는 해당 함수를 메서드로 바인딩할 객체인데, 여기서는 생성될 인스턴스 객체를 가리키는 게 this이니까 this를 아규먼트로 넘겨주면 된다.

반면 화살표 함수로 정의한 경우 상위 스코프의 this를 따르므로 따로 this 바인딩을 해주지 않아도 된다.

Ref

위키북스 - 모던 자바스크립트 Deep Dive

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함