[JS] 클래스 / 상속

Yuno·2021년 5월 12일
0

모던JS

목록 보기
5/16
post-thumbnail

동일한 종류의 여러 객체를 생성하기 위해서,
생성자 함수와 new 연산자를 통해 만들고, prototype 프로퍼티를 통해 상속할 수 있었습니다.

ES6에서 도입된 클래스 문법을 사용하면, OOP에서 사용하는 클래스 기능을 JS에서 사용할 수 있습니다.

클래스

생성문법

class MyClass {
  constructor() {...}
  method1() {...}
  method2() {...}
  method3() {...}
}

생성후에 new MyClass()를 호출하면, 정의한 메서드가 포함 된 객체가 생성됩니다.
constructor는 new에 의해 자동 호출되어 객체를 초기화 할 수 있습니다.

클래스란

자바스크립트에서 클래스는 실제로는 함수의 한 종류입니다.

class MyClass {}
alert(typeof MyClass) // function

class 문법이 하는 일
1. MyClass라는 이름의 함수를 만듭니다. 본문을 constructor에서 가져옵니다.
2. class내에서 정의한 메서드를 MyClass.prototype에 저장합니다.

따라서, new MyClass로 생성된 객체는 메서드를 사용할 수 있습니다.

기존 생성자 함수와의 차이

하는일이 위와 같다면, 생성자 함수와의 차이가 없다고 느낍니다.
그래서, 편의 문법 정도로 생각하기도 합니다.

하지만, 몇가지 차이가 있습니다.

  • 클래스로 생성된 함수는 new로만 호출할 수 있습니다.

생성자함수는 new가 아니더라도 호출이 가능합니다.
하지만, 클래스로 만든 함수에는 내부 프로퍼티 [[FunctionKind]]:classconstructor가 붙습니다.

자바스크립트는 이 프로퍼티를 확인하여, new와 함께 호출하지 않으면 에러가 발생하도록 합니다.

  • 클래스 메서드는 열거할 수 없습니다.

클래스의 프로토타입에 추가된 메서드의 enumerable플래그는 false입니다.

  • 클래스는 항상 엄격모드로 실행됩니다.

이러한 차이가 있어 단지 편의 문법이 아니라는 것을 알 수 있습니다.

클래스 표현식

함수처럼 클래스도 다른 표현식 내부에서 정의, 전달, 반환 할당할 수 있습니다.
표현식

let User = class{
	...
}

때문에, 기명 함수 표현식과 유사하게 클래스 표현식에도 이름을 붙힐 수 있습니다.

let User = class Myclass{
	...
}

동적으로 선언하거나, 반환할 수 있습니다.

getter와 setter

리터럴 객체처럼, 클래스도 getter와 setter를 포함할 수 있습니다.

class User{
    get name() {
    	return this._name;
    }
  
    set name(value) {
    	if(value.length <4) {
        	alert('너무 짧습니다')
    	}
      
      	this._name = value;
    }
}

계산된 메서드명

[]를 사용해 computed method name을 만들 수 있습니다.

class User() {
    [Symbol.xxxx]() { ... }
}

클래스 상속

프로토타입 상속처럼, 클래스도 기존 클래스를 확장하여 만들려고 할 때, 상속을 사용합니다.

extends

class Animal{
    constructor(name) {
        this.speed = 0;
        this.name = name;
    }
    run(speed) {
        this.speed = speed;
        alert(`${this.name} 은/는 속도 ${this.speed}로 달립니다.`);
    }
    stop() {
        this.speed = 0;
        alert(`${this.name} 이/가 멈췄습니다.`);
    }
}

새로 만들 클래스 RabbitAnimal을 확장하여 만듭니다.
이러면 Animal의 기능을 모두 할 수 있습니다.

class Rabbit extends Animal {
    hide() {
        alert(`${this.name} 이/가 숨었습니다!`);
    }
}

클래스 Rabbithide 메서드에도, run같은 Animal메서드에도 접근할 수 있습니다.

extends 뒤에 표현식이 올 수 있습니다.
부모 클래스를 만들어주는 함수를 호출할 수 있습니다.

메서드 오버라이딩

위 처럼 클래스 RabbitAnimal의 메서드를 사용할 수 있습니다.
그런데, Rabbit에서도 run과 같은 메서드를 정의하면, 상속받은 메서드가 아닌 자체 메서드가 사용됩니다.

class Rabbit extends Animal {
    hide() {
        alert(`${this.name} 이/가 숨었습니다!`);
    }
    run() {
    	...
    }
}

개발을 하다보면, 부모 메서드의 기능에서 일부를 확장하고 싶을 수 있습니다.
이때 super를 사용합니다.

  • super는 부모클래스에 정의된 메서드를 호출합니다.
  • super(...)은 부모 생성자를 호출하며, 자식 생성자에서만 사용합니다.
class Rabbit extends Animal {
    hide() {
        alert(`${this.name} 이/가 숨었습니다!`);
    }
    stop() {
    	super.stop();
      	this.hide();
    }
}

생성자 오버라이딩

상속받은 클래스가 생성자가 없으면, 비어있는 생성자가 만들어집니다.

class Rabbit extends Animal{
    constructor(...args) {
    	super(...args)
    }
}

상속 받은 클래스도 생성자를 정의하면, 오버라이딩됩니다.
다만, 상속 받은 클래스의 생성자에서는 반드시 super(...)가 호출되어야 합니다.
super(...)this 이전에 호출해야 합니다.

클래스 생성자 함수의 내부 프로퍼티를 통해 상속 여부를 구분합니다. [[ConstructorKind]]:"derived"

기존 생성자 함수를 호출하면, 빈 객체를 만들고, this에 할당하는 일을,
상속 받은 생성자 함수에서는 부모의 클래스가 처리하길 기대합니다.

때문에, this이전에 super()를 먼저 호출해야 합니다.

profile
web frontend developer

0개의 댓글