본문 바로가기
Archive

클래스 문법

by livemehere 2022. 9. 3.

클래스란 무엇일까?

클래스는 객체지향 프로그래밍에서 객체를 생성하기 위해 변수와 메서드를 정의하는 일종의 틀로, 객체를 정의하기 위한 상태와 메서드로 구성된다.

 

개발을 하다보면 물건같이 동일한 객체를 여러개 생성해야하는 경우가 생긴다.

javascript 에서는 객체를 생성하는 생성자 함수 new + function 을 사용할 수 있다.

그러나 객체지향 프로프로그래밍을 하기에는 불편한 점이 있었고, 이후에 도입된 class 문법을 사용하면 객체지향 프로그래밍에서 사용되는 다양한 기능을 javascript에서도 구현할 수 있게 되었다. (즉, 생성자함수 !== class)

이후에 추가된 문법답게 기존의 prototype을 내부적으로 활용하는데 흥미로웠다.

 

기본문법

new 클래스() 를 호출하면 내부에서 정의한 메서드가 들어있는 객체가 생성된다.

new 클래스()에 의해서 constructor() 생성자 메서드는 자동 호출된다.

class Person {
  constructor() {}
  walk() {}
  eat() {}
}

const kong = new Person();

 

위 문법은 아래의 생성자 함수와 동일하다.

즉, constructor() 은 생성자함수와 동일하다.

function Person() {}
Person.prototype.walk = () => {};
Person.prototype.eat = () => {};

const kong = new Person();

클래스문법과 생성자함수로 같은것을 표현할 수 있는데, 이로써 class 문법이 해주는 역할은

1. constructor(){} 생성자 함수 호출하여 객체를 생성한다.

2. 클래스 내에서 정의한 메서드를 클래스.prototype 에 저장한다.

 

 

그리고 자바스크립트에서 클래스는 함수이다.

console.log(typeof Person); // function
class Person {
  constructor() {}
  walk() {}
  eat() {}
}

console.log(Person === Person.prototype.constructor); // true
console.log(Object.getOwnPropertyNames(Person.prototype)); // [ 'constructor', 'walk', 'eat' ]

class 와 단순 생성자함수의 차이

[[IsClassConstructor]]: true

의 여부이다.

class 문법은 new 연산자없이는 에러가 발생하는데, 위 속성이 있기 때문이다.

 

클래스 필드

클래스를 정의할때, 정의한 필드는 생성된 객체에만 설정된다.

class User {
  name = "보라";
}

let user = new User();
alert(user.name); // 보라
alert(User.prototype.name); // undefined

필드로 바인딩된 메서드 만들기

메서드가 this를 잃어버리는 경우

class Button {
  constructor(value) {
    this.value = value;
  }

  click() {
    alert(this.value);
  }
}

let button = new Button("안녕하세요.");

setTimeout(button.click, 1000); // undefined

 

필드로 화살표 함수로 this를 바인딩하는 경우

class Button {
  constructor(value) {
    this.value = value;
  }
  click = () => {
    alert(this.value);
  }
}

let button = new Button("안녕하세요.");

setTimeout(button.click, 1000); // 안녕하세요.

생성자 오버라이딩

상속받은 클래스는 기본적으로 부모의 생성자를 호출한다.

하지만 명시적으로 생성자를 지정할 경우 super()를통해 부모의 생성자를 호출해주어야한다.

class Rabbit extends Animal {
  constructor(name) {
    super(name);
  }
  hide() {
    console.log(`${this.name}가 숨었습니다!`);
  }

  stop() {
    setTimeout(() => super.stop(), 1000); // 1초 후에 부모 stop을 호출합니다.
  }
}

let rabbit = new Rabbit("흰 토끼");
console.log(rabbit);
생성자를 생략할 경우 super(...args) 로 모든 인자가 super로 넘어간다.

필드 오버라이딩

필드도 자식이 오버라이딩을 할 수 있는데,

부모 생성자에서는 오버라이딩의 유무에 관계없이 반드시 부모의 필드값을 사용한다.

class Animal {
  name = 'animal'

  constructor() {
    alert(this.name); // (*)
  }
}

class Rabbit extends Animal {
  name = 'rabbit';
}

new Animal(); // animal
new Rabbit(); // animal

 

이유는 상속받은 클래스는 객체를 생성할때 super()가 먼저 실행되기 때문에, 

자식의 필드값이 초기화 되기 전임으로 이런 현상이 발생한다.

 

이 현상은 오버라이딩한 필드를 부모생성자에서 사용할 때만 발생하며,

이를 해결하기 위해서는 필드 대신 메서드를 사용하거나, getter나 setter 를 사용하여 해결하면 된다.

 

그런데 실제로 사용할때는 super() 만 먼저 호출하고 그다음 프로퍼티를 덮어씌우면 예측대로 동작한다.

반응형

'Archive' 카테고리의 다른 글

내장 클래스 확장하기  (0) 2022.09.03
class의 정적 메서드, 정적 프로퍼티  (0) 2022.09.03
prototype 과 상속  (0) 2022.09.01
함수 바인딩  (0) 2022.09.01
call/apply 와 데코레이터, 포워딩  (0) 2022.09.01