클래스
ES5 일반 함수보다 ES2015/ES6 클래스를 선호하세요
기존 ES5 클래스에서는 가독성 있는 클래스 상속, 생성자 및 메서드 정의를 작성하기 어렵습니다. 만약 상속이 필요하다면(단 실제로 필요하지 않을 수 있음을 인지해야 함), ES2015/ES6 클래스를 사용하세요. 하지만 더 크고 복잡한 객체가 필요해지기 전까지는 클래스보다 작은 함수를 우선 사용해야 합니다.
나쁜 예:
javascript
const Animal = function(age) {
if (!(this instanceof Animal)) {
throw new Error("Instantiate Animal with `new`");
}
this.age = age;
};
Animal.prototype.move = function move() {};
const Mammal = function(age, furColor) {
if (!(this instanceof Mammal)) {
throw new Error("Instantiate Mammal with `new`");
}
Animal.call(this, age);
this.furColor = furColor;
};
Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};
const Human = function(age, furColor, languageSpoken) {
if (!(this instanceof Human)) {
throw new Error("Instantiate Human with `new`");
}
Mammal.call(this, age, furColor);
this.languageSpoken = languageSpoken;
};
Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};좋은 예:
javascript
class Animal {
constructor(age) {
this.age = age;
}
move() {
/* ... */
}
}
class Mammal extends Animal {
constructor(age, furColor) {
super(age);
this.furColor = furColor;
}
liveBirth() {
/* ... */
}
}
class Human extends Mammal {
constructor(age, furColor, languageSpoken) {
super(age, furColor);
this.languageSpoken = languageSpoken;
}
speak() {
/* ... */
}
}메서드 체이닝 사용
이 패턴은 jQuery와 Lodash 등 많은 자바스크립트 라이브러리에서 활용되는 매우 유용한 패턴입니다. 코드의 표현력을 높이고 장황함을 줄이기 때문에 체이닝을 적극 권장합니다. 클래스 함수 끝에 this를 반환하는 간단한 방법으로 메서드 체이닝을 구현할 수 있습니다.
나쁜 예:
javascript
class Car {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}
setMake(make) {
this.make = make;
}
setModel(model) {
this.model = model;
}
setColor(color) {
this.color = color;
}
save() {
console.log(this.make, this.model, this.color);
}
}
const car = new Car("Ford", "F-150", "red");
car.setColor("pink");
car.save();좋은 예:
javascript
class Car {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}
setMake(make) {
this.make = make;
// NOTE: Returning this for chaining
return this;
}
setModel(model) {
this.model = model;
// NOTE: Returning this for chaining
return this;
}
setColor(color) {
this.color = color;
// NOTE: Returning this for chaining
return this;
}
save() {
console.log(this.make, this.model, this.color);
// NOTE: Returning this for chaining
return this;
}
}
const car = new Car("Ford", "F-150", "red").setColor("pink").save();상속보다 조합(Composition) 우선시
Gang of Four의 <디자인 패턴> 서적에서 유명한 원칙대로, 가능하다면 상속 대신 조합을 선택해야 합니다. 두 방식 모두 장점이 있지만 상속을 고려하기 전에 항상 조합으로 문제를 해결할 수 있는지 검토해야 합니다. 이 접근법은 더 나은 설계를 가능케 하는 경우가 많습니다.
다음과 같은 질문을 가질 수 있습니다: "언제 상속을 사용해야 하나요?" 이는 현재 문제에 따라 다르지만, 다음은 상속이 조합보다 적합한 경우를 정리한 목록입니다:
- 상속 관계가 "is-a" 관계이고 "has-a" 관계가 아닌 경우 (Human->Animal vs. User->UserDetails)
- 기본 클래스 코드 재사용이 가능할 때 (모든 동물처럼 인간도 이동 가능)
- 기본 클래스 수정으로 모든 파생 클래스에 전역 변경을 적용하려는 경우 (동물 이동 시 칼로리 소비량 일괄 조정)
나쁜 예:
javascript
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
// ...
}
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
constructor(ssn, salary) {
super();
this.ssn = ssn;
this.salary = salary;
}
// ...
}좋은 예:
javascript
class EmployeeTaxData {
constructor(ssn, salary) {
this.ssn = ssn;
this.salary = salary;
}
// ...
}
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
setTaxData(ssn, salary) {
this.taxData = new EmployeeTaxData(ssn, salary);
}
// ...
}