Languages/JavaScript

[HUFS/GnuVil] #13 클래스

성중 2022. 10. 18. 16:09

클래스

자바스크립트는 프로토타입 기반 객체지향 언어로, 인스턴스 생성에 생성자 함수가 있어 클래스가 굳이 필요 없었지만 ES6에 추가된 클래스 문법은 다음과 같은 몇 가지 차별점이 있다

  • 생성자 함수와 달리 new 없이 호출하면 에러가 발생한다
  • 상속을 지원하는 extends와 super 키워드를 제공한다
  • 함수/변수 호이스팅이 발생하지 않는 것처럼 동작한다 (TDZ)
  • 클래스 내의 모든 코드에 암묵적으로 strict mode가 지정된다
  • constructor 및 메서드들의 [[Enumerable]] 값이 false로, 열거되지 않는다

 

// 클래스 선언문
class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name; // name 프로퍼티는 public하다.
  }

  // 프로토타입 메서드
  sayHi() {
    console.log(`Hi! My name is ${this.name}`);
  }

  // 정적 메서드
  static sayHello() {
    console.log('Hello!');
  }
}

// 인스턴스 생성
const me = new Person('Lee');

// 인스턴스의 프로퍼티 참조
console.log(me.name); // Lee
// 프로토타입 메서드 호출
me.sayHi(); // Hi! My name is Lee
// 정적 메서드 호출
Person.sayHello(); // Hello!
  • constructor(생성자): 인스턴스의 프로퍼티를 정의한다
  • 프로토타입 메서드: 인스턴스가 호출 가능한 메서드를 정의하며 인스턴스 프로퍼티를 참조할 수 있다
  • 정적(static) 메서드: 클래스가 호출 가능한 메서드를 정의한다

* 프로토타입 메서드와 정적 메서드는 속해 있는 프로토타입 체인이 다름

 

클래스 역시 함수이자 일급객체
프로토타입 메서드
정적 메서드

// 표준 빌트인 객체의 정적 메서드
Math.max(1, 2, 3);          // -> 3
Number.isNaN(NaN);          // -> true
JSON.stringify({ a: 1 });   // -> "{"a":1}"
Object.is({}, {});          // -> false
Reflect.has({ a: 1 }, 'a'); // -> true

표준 빌트인 객체의 정적 메서드들을 활용하자!

 

class Person {
  // 생성자
  constructor(name) {
    // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
    console.log(this); // Person {}
    console.log(Object.getPrototypeOf(this) === Person.prototype); // true

    // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
    this.name = name;

    // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
  }
}

생성된 인스턴스는 constructor에서 암묵적으로 this에 바인딩 및 반환된다

 

class Person {
  // 클래스 필드
  name = 'Lee';

  constructor() {
    console.log(name); // ReferenceError: name is not defined
  }
}

new Person();

클래스 몸체에서 클래스 필드를 정의해줄 수도 있으며 클래스 내에서 참조하려면 this를 사용해야 한다. 클래스 필드에 함수를 할당하는 것은 권장하지 않는다 (프로토타입 메서드와 혼동)

 

class Person {
  // private 필드 정의
  #name = '';

  constructor(name) {
    // private 필드 참조
    this.#name = name;
  }
}

const me = new Person('Lee');

// private 필드 #name은 클래스 외부에서 참조할 수 없다.
console.log(me.#name);
// SyntaxError: Private field '#name' must be declared in an enclosing class

#을 붙여 클래스 내부에서만 참조할 수 있는 private 필드를 정의할 수 있다

* 상속된 자식 클래스 내부나 클래스 인스턴스를 통한 접근 불가능

 

class Animal {
  constructor(age, weight) {
    this.age = age;
    this.weight = weight;
  }

  eat() { return 'eat'; }

  move() { return 'move'; }
}

// 상속을 통해 Animal 클래스를 확장한 Bird 클래스
class Bird extends Animal {
  fly() { return 'fly'; }
}

const bird = new Bird(1, 5);

console.log(bird); // Bird {age: 1, weight: 5}
console.log(bird instanceof Bird); // true
console.log(bird instanceof Animal); // true

console.log(bird.eat());  // eat
console.log(bird.move()); // move
console.log(bird.fly());  // fly

클래스 상속으로 코드 중복을 줄일 수 있다

 

 내용은 위키북스의 '모던 자바스크립트 Deep Dive' 바탕으로 작성되었습니다.