class는 es6의 문법이 syntactic sugar로서, function을 활용해 상속을 구현한 객체이다.
하지만 내부적으로 자세히 살펴보면, 그저 상속 구조를 쉽게 이해하고 구현할 수 있는 특징외에도 다른 특징들이 존재한다.
class 문법을 사용한 코드 그리고 function만을 사용해 변환한 코드를 보며 차이점을 알아보자.
class Animal {
constructor (name, age) {
this.name = name
this.age = age
}
setLegs(param) {
this.legs = param
}
}
class Cat extends Animal {
constructor (name, age) {
super(name, age)
}
setIsStray(isStray) {
this.isStray = isStray
}
}
내부에 선언된 멤버변수와 멤버함수는 public하다.
내부에 선언된 함수들은 프로토타입의 멤버로서 존재한다.
클래스 내부 스코프에 strict mode가 자동으로 설정된다.
* strict mode는 잠재적인 에러 및 최적화를 위해 기존의 동작과 차이를 둔 문법이다.
ex) 잠재적인 문제들을 throwing, 미래의 예약어 사용 제한
typescript와 조합하면 멤버들을 private, protected하게 정의할 뿐만 아니라 interface를 구현하는 방식으로도 사용할 수 있다.
이밖에도 정의 전에 사용할 수 없으며, 재정의가 되지 않는다.
ReferenceError: Cannot access 'Cat' before initialization
에러가 발생한다.위의 클래스와 동일한 상속 구조를 가지면서도 function으로 구현한 모습이다. new 연산자를 통해 생성자 함수를 호출하면 this는 생성된 인스턴스를 가리켜, 연결된 멤버 및 함수에 접근할 수 있다.
function Animal(name, age) {
this.name = name
this.age = age
}
Animal.prototype.setLegs = function (param) {
this.legs = param
}
function Cat(name, age) {
Animal.call(this, name, age)
}
// object.create or setPrototypeof을 통해 체인 만들어주기
Cat.prototype = Object.create(Animal.prototype, {
constructor: {
value: Cat,
// writable: false(default), configurable: false(default)
},
})
Cat.prototype.setIsStray = function (param) {
this.isStray = param
}
모든 엔진에서 지원한다. (IE 5.5까지도 지원한다고 한다)
기본적으로 함수 생성자 내에 정의된 멤버변수와 함수들은 인스턴스마다 독립적인 메모리 공간에 할당된다.
이를 위해 공통 메모리에 저장될 멤버 및 함수들은 prototype에 접근하여 설정한다.
클로져를 활용해 private 멤버변수 및 멤버함수를 정의할 수 있다.
name, age는 외부 스코프에서 접근가능한데 이를 아래처럼 변경하면 클로져의 특성으로 인해 getName에서만 name에 접근가능하게 되며 은닉화를 달성할 수 있다.
정의 전에 사용할 수 있다.
function Animal(name, age) {
let _name = name
this.age = age
this.getName = function () {
return _name
}
}
class는 함수를 활용해 만들어진 문법이며 그렇기 때문에 prototype을 이용해 function만으로 class의 상속을 구현하며 공통 멤버변수 개별 멤버변수 정의 등이 가능하다.
최신 런타임 환경에서 private이 지원되고 있고, public과 static, interface 등이 예약어로 등록되어있는 것을 보아 ts에만 지원하는 기능들이 js의 class에도 추가될 것으로 보인다. (이 중 static은 지원되고있다)
js의 class문법은 다른 객체지향프로그래밍 언어와 많이 닮아있다. 따라서 다양한 언어로 개발하는 환경에서는class를 사용하는 것이 의사소통에 좀 더 이로울 것 같다. 또한 상속을 위한 보일러 플레이트 코드도 크지않다. static, private 키워드가 지원되어 class 구조를 이해하기에도 용이할 것이다.
또 이러한 class를 이용하는게 최선의 선택일까?에 대한 나의 생각을 공유해보자면,
응집된 여러 메소드들의 관리, 내부적인 상태 관리 등 객체 지향 프로그래밍 방식으로 구현하는게 유지보수성에 득이 된다면 class문법을 이용해 객체지향 프로그래밍 방식을 적용하는 게 좋아보인다.
반면 객체 지향 프로그래밍에서 객체는 가변성을 가지며, 코드를 예측하기 어렵게 만든다는 단점을 갖고있다. 따라서 class를 남용하는 것에 대해서 견제해야 할 필요가 있겠다.
모던 javascript deep dive - 25장 클래스