더글라스 크락포드의 자바스크립트 핵심가이드를 정리한 문서입니다.
총 10장으로 이루어진 책으로 한 장씩 정리하여 포스팅할 예정입니다.
본 책은 자바스크립트의 좋은 점만을 정리한 책으로, 나쁜 점을 잊어야 할 수고를 덜 수 있습니다.
상속이란?
상속이란 반복적인 코드를 작성하지 않고 코드를 재 사용하는 방법을 의미합니다.코드를 재 사용함으로써 소프트웨어 개발기간과 시간을 단축할 수 있습니다.
객체지향 언어는 상속에 따라서 크게 두 가지로 나눌 수 있습니다.
- 클래스 기반 언어: Java, Python, Ruby, C++, C#
- 프로토타입 기반 언어: Javascript, JScript, ActionScript
클래스 기반언어에서 클래스는 객체의 형식이 정의되어 있고, 클래스를 활용하여 객체(Instance)
를 생성합니다.
프로토타입 기반언어에서는 클래스라는 개념이 없고, 객체에서 객체를 바로 상속할 수 있습니다.
Before we deep-dive into code…
예제코드에서 핵심적인 부분으로 다뤄지는 Object.create()메소드
와 new 연산자
에 대해서 먼저 자세히 보는것이 중요합니다.
새로운 객체를 생성하고, 이 객체에 프로토타입속성을 지정해서 반환해주는 역할을 동일하고, Object.create()
메소드는 매개변수로 객체를 받고, new 연산자는 생성자를 받는 차이만 있습니다.
Object.create()
메소드는 새 객체를 만들고 인자값으로 받은 변수를 prototype 속성에 할당합니다. 따라서, 매개변수로객체 리터럴
또는함수객체의 prototype객체
를 사용해야 합니다.
if (typeof Object.create != 'function') {
Object.create = function (obj) {
function F(){}; // 새로 만든 객체의 prototype속성에 인자로 받은 객체를 할당합니다
F.prototype = obj;
return new F();
}
}
new
연산자는 새로운 객체를 생성하고 prototype속성에생성자.prototype
객체를 할당합니다. (단, 생성자가 명시적으로 객체를 반환하는 경우 이를 반환합니다.)
실제 구현체는 감춰져있지만, new 연산자를 내부동작을 코드로 나타내면 다음과 유사합니다.
Function.prototype.new = function(){
// 생성자의 원형은 생성자.prototype이기 때문에 this.prototype으로
// 상속받아서 새로운 객체를 생성합니다
var that = Object.create(this.prototype)
// 위에서 만든 객체를 적용하여 this객체의 반환값을 받아옵니다
var other = this.apply(that, arguments); // 만약 this객체의 반환값이 객체타입이라면 other를 반환하고,
// 객체가 아니라면 맨위에서 생성한 that객체를 반환합니다
return (typeof other === 'object' && other) || that;};
Let’s dive into code!
자바스크립트에서는 크게 세가지의 상속방식을 가집니다.
- 의사 클래스 방식(Pseudo Classical Method)
- 프로토타입 방식
- 함수 방식
의사 클래스 방식은 클래스 기반 언어들의 상속형태와 유사하게 클래스를 정의 및 상속하고 객체를 생성하는 방식입니다.
프로토타입방식은 객체리터럴을 바로 상속하여 객체를 생성하는 방식입니다.
함수 방식은 함수객체에서 함수객체를 상속하여 객체를 만드는 방법입니다.
1. 의사클래스 방식(Pseudo Classical Method)
의사클래스방식은 다른말로 가짜 클래스 방식이라고 할 수 있습니다. 자바스크립트에서는 객체를 표현하기 위하여 함수를 사용하고 있습니다. 자바스크립트는 클래스가 없기 때문에 이를 흉내내기 위하여 만들어진 패턴입니다.
생성자를 만들면, 생성자객체
와 생성자.prototype객체
가 생성되고 생성자.prototype
객체는 원형을 담고 있기 때문에, 상속하고자 하는 부모생성자의 prototype
으로 변경시키는 것이 핵심입니다.
예를 위하여 먼저 Mother 생성자를 만들고, Daughter 생성자를 생성하여 상속받도록 하겠습니다.
var Mother = function() {
this.name = 'Mother';
this.ability = 'Cooking';
}
Mother.prototype.get_name = function() {
return this.name;
}
Mother.prototype.get_ability = function() {
return this.ability;
}var Daughter = function() {
this.name = 'Daughter';
this.ability = 'Programming';
}
여기까지는 독립적인 함수객체에 불과합니다. 딸 생성자는 이미 딸.prototype이 생성되어 있기 때문에 이를 엄마의 prototype으로 변경하여 get_name
과 get_ability
를 사용하도록 하겠습니다
// 딸 생성자의 프로토타입을 엄마 생성자의 프로토타입을 가진 객체로 변경!
Daughter.prototype = new Mother();var daughter = new Daughter();
daughter.get_name(); // Daughter
daughter.get_ability(); // Programming
여기서 핵심은 Daughter.prototype = new Mother();
부분입니다.
클래스 기반에 익숙한 개발자에겐 직관적이지만, new 연산자를 반드시 사용해야하며, private 속성은 사용할 수 없다는 단점이 있습니다
2. 프로토타입방식
유사 클래스방식과는 다르게, 객체에서 바로 객체로 상속할 수 있는 방식입니다.새로운 객체를 생성 후 프로토타입속성에 매개변수로 받은 객체를 할당합니다.
사용법은 Object.create()
메소드를 호출하는 것이 전부입니다. Chrome, firefox, ie9이상 등등 모던 브라우저에서는 모두 지원을 하는 내장메소드입니다. 내부 구현은 아래와 같습니다.
if (typeof Object.create != 'function') {
Object.create = function (obj) {
function F(){};
F.prototype = obj;
return new F();
}
}
새로운 함수객체를 생성 후 프로토타입에 매개변수로 받아온 객체를 할당하는 로직으로 되어있습니다.
var mother = {
name: 'Mother',
ability: 'Cooking',
get_name: function(){
return name;
},
get_ability: function(){
return ability;
}
}var daughter = Object.create(mother);
daughter.name; // Mother
daughter.get_name(); // Mother
객체를 바로 상속할 수 있는 이 방법은 자바스크립트의 특성을 잘 반영한 방법입니다. 그러나, 모든 속성이 Public하다는 것이 단점입니다.
3. 함수를 사용한 방식
이 방식은 함수객체에서 함수를 상속하는 방법입니다. 부모 함수객체에서 새로운 객체를 생성하고 상속하고자 하는 속성을 할당하고 반환합니다. 자식 함수에서 부모함수를 호출하여 상속받을 수 있습니다.
var mother = function(spec){ // 상속을 하기 위한 속성을 담는 객체를 생성합니다
var that = {}; // 상속하려는 메소드를 추가합니다
that.get_name = function(){
return spec.name;
} that.get_ability = function(){
return spec.ability;
}
// 코드 마지막에 생성했던 객체를 반환합니다
return that;
}
var mom = mother({'name':'mom', 'ability':'cooking'})
console.log(mom.get_name()); // mom
생성자가 아니기 때문에 이름은 소문자로 지정합니다. 여기서 눈여겨 볼 점은, 객체를 매개변수로 활용하고 있다는 점입니다. 이로인한 장점은, 여러 값을 주고 받을 때 순서에 신경쓸 필요가 없다는 것 입니다.
var daughter = function(spec){
spec.ability = spec.ability || 'game';
var that = mother(spec)
// mother로 부터 ability에 대한 호출 값을 가져와서 저장한다
super_get_ability = that.get_ability()//get_ability 함수를 재정의 한다
that.get_ability = function(){
return 'this is new ability '+ super_get_ability
}
return that;
}
var myDaughter = daughter({'name': 'daughter'})
console.log(myDaughter.get_name()); // son
console.log(myDaughter.get_ability()); // this is new ability game
자식 함수에서 부모를 상속하려면 그저 부모를 호출하기만 하면됩니다. 부모에서 전달한 메소드를 사용하고 override할 수 있습니다.
위에서 super_get_ability 값은 부모 속성에서 반환하는 값을 저장하는 변수에 불과합니다. 그러면, 다른 언어와 같이 부모의 메소드를 호출하는 super()메소드는 어떻게 사용할 수 있을까요?
Object.prototype.super = function(name){
// 호출한 객체를 that에 할당합니다
var that = this; // 객체에서 이름으로 메소드를 찾아옵니다
var method = that[name];
// 찾아온 메소드를 실행할 때, 호출객체와 매개변수를 활용합니다
return function(){
return method.apply(that, arguments);
};
}var daughter = function(spec){
spec.ability = spec.ability || 'game'; var that = mother(spec) // 부모객체의 메소드를 찾아서 가져온다
var super_get_ability = that.super('get_ability');//get_ability 함수를 재정의 한다
that.get_ability = function(){
return 'this is new ability '+ super_get_ability()
}
return that;
}
var myDaughter = daughter({'name': 'daughter'})
myDaughter.get_ability(); // this is new ability game
자식객체에서 override 후 that.super()으로 호출하면 재 정의된 값을 반환하기 때문에 override하기 전에 미리 변수에 할당하도록 합니다
지금까지 자바스크립트에서 가장 대표적인 상속방법 세가지를 알아보았습니다.
의사클래스방식은 생성자에서 생성자로 상속받아 이를 객체로 만드는 방법이었습니다.
함수방식은, 위와 유사하게 함수객체에서 함수객체로 상속받아 instance 객체를 만들어 사용하는 방법이었습니다. 의사클래스 방식과의 차이점은 private변수를 사용할 수 있다는 장점이있습니다.
프로토타입방식은 위와 다르게 객체 리터럴을 바로 상속받아 객체를 생성하는 방법입니다.
EMAC6
에서는 Class가 생겨서 클래스 기반 언어와 동일하게 상속이 가능해졌습니다. ;)