코어 자바스크립트를 꼼꼼하게 정독하고
복습하며 정리한 내용을 토대로 F-lab 멘토링을 받고있다.
이 글은 멘토링으로 알게된 것들을 다시한번 정리해보는 글이다.



클래스

자바스크립트는 프로토타입 기반의 언어이다.
상속의 기능을 구현하지 않으나 프로토타입으로 상속과 비슷한 구현을 가능하게 하는 문법들이 있다.
ES6에서는 클래스 문법이 추가되었는데, 이것은 여전히 프로토타입을 활용하는 것이므로
다른 언어에서의 클래스와는 조금 다르다.


클래스 기반 언어의 상속

클래스는 집합의 모든 공통 속성을 정의한 설계도 같은 것이다.
클래스에서의 상속은 부모클래스와 자식클래스 간의 관계를 말하며 계층적 관계를 만들어낸다.
하위 클래스는 상위 클래스의 모든 속성을 상속하면서
하위클래스로 갈수록 더 구체적 요건이 추가 또는 변경된다.

클래스 기반 언어에서는 클래스 생성 후 컴파일 시 인스턴스를 인스턴스화하므로
런타임 동안 클래스를 수정할 수 없다.

프로토타입 기반 언어의 상속

프로토타입 기반 언어는 클래스, 인스턴스 구분을 하지 않고 객체를 만든다.
템플릿으로 사용할 생성자함수의 프로토타입에 초기 속성을 정의하고
new 키워드와 함께 생성자함수를 사용해 새로운 객체(인스턴스)를 만든다.
또한 객체를 다른 객체의 프로토타입으로 연결해 속성을 공유할 수 있다.
클래스의 상속관계 구현을 위해 결국 prototype chaining을 이용하는 것이다.

자바스크립트에서는 런타임에 생성자함수의 프로토타입에 속성을 수정하면
하위 프로토타입(인스턴스)도 수정된다.

Class-based vs prototype-based languages


프로토타입 메서드 vs 스테틱 메서드

자바스크립트의 인스턴스는 생성자의 프로토타입 객체를 참조하므로 참조의 결과가 다를 수 있다.

프로토타입 메서드는 인스턴스가 자신의 것 처럼 사용할 수 있지만
스테틱 메서드는 인스턴스가 호출할 수 없고 클래스(생성자함수)만 호출할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
const Rectangle = function (width, height) {
  this.width = width;
  this.height = height;
};

Rectangle.prototype.getArea = function () {
  return this.width * this.height;
}; // 프로토타입 메서드

Rectangle.isRectangle = functio(instance) {
    return instance instanceof Rectangle
} // 스테틱 메서드

이렇게 프로토타입 메서드와 스테틱 메서드가 선언된 생성자함수로 인스턴스를 생성하고
getAreaisRectangle 메서드를 호출해보면 아래와 같은 결과를 얻을 수 있다.

1
2
3
4
5
const rec = new Rectangle(3, 4);

console.log(rec.getArea()); // 12
console.log(rec.isRectangle(rec)); // Error
console.log(Rectangle.isRectangle(rec)); // true

인스턴스는 생성자의 스테틱 프로퍼티와 스테틱 메서드에 접근할 수 없고
생성자함수가 직접 접근해야하는 스테틱메서드를 호출할때 생성자함수는 객체로서 취급된다.


ES6의 클래스와 상속

ES6에서 클래스 문법이 도입되며 extends 키워드만으로 상속관계 설정을 끝낼 수 있게 되었다.
또한 super 함수를 사용해 상위 클래스의 constuctor를 실행시킬 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  constructor(width) {
    super(width, height);
  }
}

const rec = new Square(4);
console.log(rec); // Square { width: 4, height: 4 }
console.log(rec.getArea()); // 16


추가학습 - 프로토타입으로 클래스 구현

프로토타입을 활용해 클래스의 상속 기능을 구현해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Class
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  getArea() {
    return `Size: ${this.width * this.height}`;
  }
}

class Square extends Rectangle {
  constructor(width, color) {
    super(width, width);
    this.color = color;
  }
  getColor() {
    return `Color: ${this.color}`;
  }
}

const rec = new Square(4, "Red");
console.log(rec.getArea()); // Size; 16
console.log(rec.getColor()); // Color: Red
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Rectangle = function (width, height) {
  this.width = width;
  this.height = height;
};
Rectangle.prototype.getArea = function () {
  return `Size: ${this.width * this.height}`;
};

const Square = function (width, color) {
  Rectangle.call(this, width, width);
  this.color = color;
};
Square.prototype = Object.create(Rectangle.prototype);
Square.prototype.getColor = function () {
  return `Color: ${this.color}`;
};

const rec = new Square(4, "Red");
console.log(rec.getArea()); // Size: 16
console.log(rec.getColor()); // Color: Red

Square 생성자로 만든 인스턴스가 Rectangle 생성자의 속성과 프로토타입 메서드를 활용할 수 있도록
Rectangle 함수 내 속성은 Rectangle에 Square의 this를 바인딩시키고
Square의 프로토타입에 Rectangle의 프로토타입을 할당함으로서 연결시켰다.

따라서 Square의 인스턴스가 Rectangle의 프로토타입 메서드를 사용할 수 있게 되었다.


Leave a comment