우선 들어가기 전, 자바스크립트는 프로토타입 기반 객체 지향 언어이다. 모든 객체는 각 부모 역할을 담당하는 객체와 prototype
으로 연결이 되어 있다. 때문에 class가 없어도 객체 지향 프로그래밍이 가능하다.
class가 분명히 제공해주는 부분이 있다. 기존에 사용했던 prototype
기반의 상속 보다 class는 명료하게 구현되고, 객체를 생성하고 상속을 다루는데 있어 조금 더 명확한 문법들을 제공해준다.
❗ 하지만 자바스크립트의 class는 새로운 객체지향 상속 모델이 추가된 게 아니라 prototype
기반과 연결되어 있으며, class도 하나의 함수이며, 기존 prototype
기반 패턴을 class
기반 패턴처럼 사용할 수 있도록 하는 문법적 설탕이라 볼 수도 있다.
둘다 prototype
기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지 않는다. 클래스틑 생성자 함수보다 엄격하며, 생성자 함수에서 제공하지 않는 기능도 제공한다.
클래스 | 생성자 함수 | |
---|---|---|
new 연산자 미 선언 | 에러 발생 | 일반 함수로서 호출 |
extends, super | O | X |
호이스팅 | 발생하지 않는 것처럼 동작 | 함수 선언문: 함수 호이스팅 함수 표현식 : 변수 호이스팅 |
엄격모드 | O | X |
프로토타입 객체지향을 구현했다는 점에서 유사하지만, 클래스틑 생성자 함수 기반의 객체 생성 방식보다 견고하고 명료하다. (extends, super 키워드는 상속 관계 구현을 더욱 간결하고 명료하게 한다.)
따라서, 자바스크립트에서의 클래스는 새로운 객체 생성 메커니즘 이라고 생각할 수 있다.
// 클래스 선언
class Person {}
// 익명 클래스 표현식
const Person = class {};
// 클래스 선언문
class Person {
// 생성자
constructor(name) {
// 인스턴스 생성 및 초기화
this.name = name;
}
// 프로토타입 메소드
sayHi() {
console.log(`Hi name is ${this.name}`);
}
// 정적 메소드
static sayHello() {
console.log('hello');
}
}
// 인스턴스 생성
const me = new Person('Lee');
// 프로퍼티 참조
console.log(me.name); // Lee
// 프로토타입 메소드 호출
me.sayHi(); // Hi name is Lee
// 정적 메소드 호출
Person.sayHello(); // hello
선언문으로 정의한 클래스는 함수 선언문과 같이 런타임 이전에 평가되어 함수 객체를 생성한다. 클래스는 호이스팅이 발생하지 않는 것처럼 보이나 그렇지 않다.
const Person = '';
{
// 호이스팅이 발생하지 않으면 ''출력
console.log(Person); // 에러
// 클래스 선언문
class Person {}
}
클래스 선언문도 변수 선언, 함수 정의처럼 호이스팅이 발생한다. 단 클래스는 변수 선언처럼 호이스팅이 된다. 일시적인 사각지대에 빠지기 때문에 호이스팅이 발생하지 않는 것처럼 동작한다.
따라서 var, let, const, function, class 선언된 모든 식별자는 런타임 이전에 먼저 실행되기 때문에 호이스팅이 일어난다고 알아두기
클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다. new 연산자 사용 여부에 따라 일반 함수 / 생성자 함수로 호출되는데, 클래스는 인스턴스 생성을 위해 만들어진 이유도 있기 때문에 반드시 new 연산자와 호출한다.
const Person = class MyClass {};
// 클래스를 가르키는 식별자로 인스턴스 생성해야 함.
const me = new Person();
// MyClass는 함수와 동일하게 클래스 몸체 내부에서만 유효한 식별자
console.log(MyClass); // 에러
const you = new MyClass(); // 에러
extends
는 클래스의 자식 클래스를 생성할때 사용한다. 상속을 통해 클래스를 확장하려면 extends
키워드를 사용하여 상속받을 클래스를 정의한다.
// 생성자 함수로 선언된 수퍼(베이스/부모) 클래스
class Base(a) {
this.a = a;
}
// 상속받은 서브(파생/자식) 클래스
class Derived extends Base {}
const derived = new Derived(1);
console.log(derived); // Derived {a: 1}
super
은 부모 생성자를 호출한다. 함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드이다.
// 수퍼(베이스/부모) 클래스
class Base {
constructor(a,b) {
this.a = a;
this.b = b;
}
}
// 서브(파생/자식) 클래스
class Derived extends Base {
// 암묵적으로 constructor가 정의된다.
constructor(a, b, c) {
super(a, b);
this.c = c;
}
}
const derived = new Derived(1, 2, 3);
console.log(derived); // Derived {a: 1, b: 2, c: 3}
상속 관계의 클래스가 인스턴스를 생성하는 과정을 보며 super의 사용법 익혀보쟈!
// 수퍼 클래스
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
getArea() {
return this.width * this.height;
}
toString() {
return `whidth = ${this.whidth}, heigth = ${this.height}`
}
// 서브 클래스
class ColorRectangle extends Rectangle {
constructor(width, height, color) {
super(width, height);
this.color = color;
}
// 메서드 오버라이딩
toString() {
return super.toString() + `, color = ${this.color}`
}
}
const colorRectangle = new ColorRectangle(2, 4, 'red');
console.log(colorRectangle); // ColorRectangle {width: 2, height: 4, color: 'red'}
// 상속을 통해 getArea 메서드 호출
console.log(colorRectangle.getArea()); // 8
// 오버라이딩된 toString 메서드 호출
console.log(colorRectangle.toString()); // width = 2, height = 4, color = red
class가 생성자 함수를 완벽히 대체한다고 보기 힘들지만, 꼼꼼한 코드를 위해 class를 지향해보쟈