HorseClass
와 FlyingHorseClass
클래스를 ES6 class 상속의 방법으로 리팩토링하세요.let HorseClass = function (name) {
this.name = name
}
HorseClass.prototype.goSomewhere = function (destination) {
return `${this.name} is galloping to ${destination}!`
}
let FlyingHorseClass = function (name, color) {
HorseClass.apply(this, [name])
this.color = color;
}
FlyingHorseClass.prototype = Object.create(HorseClass.prototype)
FlyingHorseClass.prototype.constructor = FlyingHorseClass
FlyingHorseClass.prototype.goSomewhere = function (destination, milesToDestination) {
if (milesToDestination < 10) {
return HorseClass.prototype.goSomewhere.call(this, destination)
} else {
return `${this.name} is flying to ${destination}!`
}
}
// -----------------------START!!-----------------------
class Horse {
// FILL ME IN
}
class FlyingHorse extends Horse {
// FILL ME IN
}
일단 pseudo-classical한 방식으로 작성된 코드를 살펴본다.
이것과 동일한 기능을 Class 문법을 이용해 짜라는 것인데, 사실 Class 와 상속 은 이전 과제에서 한바탕 데이면서 공부해놓은게 머릿속에 아직 남아 있어서 아주 막 곤란하지는 않았다.
다만, HorseClass 의 goSomewhere 메소드를 FlyingHorseClass 의 메소드에 오버라이딩하는 부분에서 좀 애를 먹었다. 무엇이 문제였을까?
보통은 엄마가 만들어둔 반찬은 나도 먹을 수 있고, 내 입맛에 맞게 반찬의 간을 조절해서 먹기도 한다.
그와 똑같이, 상위 클래스가 정의한 메소드는 자식 클래스도 사용할 수 있고, 자식 클래스가 제 입맛에 맞게 재정의해서 사용할 수도 있다. 오버라이딩(overriding)
이라고 한다.
이름이 되게 있어보이지만 오버라이딩을 하기 위한 특별한 비기가 있거나 한건 아니고, 그 방법은 그냥 평범하게 같은 이름의 메소드를 내용만 다르게 해서 정의하면 된다.
// 일반적인 constructor 선언과 메소드 설정
class Horse {
constructor(name) {
this.name = name
}
goSomewhere(destination) {
return `${this.name} is galloping to ${destination}!`
}
}
class FlyingHorse extends Horse {
constructor(name, color) {
// 부모클래스에 이미 정의된 this.name=name 을 super 키워드를 이용해 가져다 썼다.
super(name)
this.color = color
}
// 아래 메소드는 부모클래스에 정의된 것과 동일한 이름을 갖고 있지만, 둘은 별개의 메소드이다.
goSomewhere(destination, milesToDestination) {
if (milesToDestination < 10) {
// 부모클래스의 goSomewhere 메소드를 super 키워드를 이용해 가져다 썼다.
return super.goSomewhere(destination)
} else {
return `${this.name} is flying to ${destination}!`
}
}
}
기본적인 클래스 구현방법 외에 크게 살펴볼 만한 구석은 없는 것 같아서, 코드에 해단 해설은 넘어간다.
["Jamie Vardy plays soccer", "James Maddison plays soccer"]
let LeisterCity = {
name: 'soccer',
players: [
{ name: 'Jamie Vardy', age: 34 },
{ name: 'James Maddison', age: 24 }
],
playerNames: // FILL ME IN
};
this 키워드는 아직 부담스럽다. 맥락 이라는게 딱 직관적으로 경계선이 그어지는게 아니다보니 그렇다. 그래도 이 문제에서는 그다지 this때문에 문제된건 없었다. 바로 풀이로 간다!
let LeisterCity = {
name: 'soccer',
players: [
{ name: 'Jamie Vardy', age: 34 },
{ name: 'James Maddison', age: 24 }
],
playerNames: function() {
return this.players.map( n => `${n.name} plays ${this.name}` )
}
};
players 배열의 내용을 map 메소드를 이용해 함수의 적용을 받은 상태로 바꾼 다음 리턴하는 메소드를 만들었다. 여기서 눈 여겨볼 점은 어디에는 this 가 붙어있고 어디에는 n 이 붙어있는데, 이게 무슨 기준으로 이루어진 것인지? 하는 부분이다.
playerNames 라는 메소드를 그냥 일개 함수라고 생각할 필요가 있다. 얘는 호출되기 전까지는 '내가 객체 내부에 존재하는지 그냥 혼자 덩그러니 있는지 어떤지 알게 뭐냐' 하고 있다가, 호출되고 나서야 자기가 어디있는 누구인지 살펴보기 시작한다. 따라서 함수를 선언하는 단계에서 this.players
, this.name
이라고 명시해주어야 나중에 이 함수가 호출되었을 때 누가 자신을 부른건지 확인하고 this를 처리할 수 있게 된다.
한편, n.name
의 경우에는 사정이 좀 다르다. playersNames 메소드야 경우에 따라 LeisterCity 객체가 아닌 다른 객체로부터 호출될 수 있으니 this 가 달라질 수 잇다고 해도, players 배열의 요소를 돌면서 그 요소의 name 을 조회하겠다는 내용은 호출자가 백번 달라져도 불변이므로 this 를 쓸 필요없이 n.name
으로 해결되는 것이다.