이번에는 Javascript OOP 의 핵심인 Class에 대해 알아보도록 할까요.
사실 prototype 프로퍼티에 이어 붙이는 식이지만 다음의 소스를 보시면 놀랄정도로 객체 지향의 느낌을 가지고 있습니다.
Prototype Javascript 프레임워크가 놀라운 이유중에 가장 큰 부분이 아닐까 생각해 봅니다.
1. Class
클래스 생성의 기본을 한번 봐볼까요.
[code]var Animal = Class.create({
initialize: function(name, sound) {
this.name = name;
this.sound = sound;
},
speak: function() {
alert(this.name + ” says: ” + this.sound + “!”);
}
});[/code]
Class.create를 사용하여 클래스를 생성하며 인자로는 정의한 메서드들을 넣습니다.
눈치 채셨겠지만 initialize는 생성자입니다.
여기서 상속까지 한번 해볼까요?
[code]// Animal을 상속
var Snake = Class.create(Animal, {
initialize: function($super, name) {
$super(name, ‘hissssssssss’);
}
});[/code]
Snake라는 클래스를 생성하였습니다. 그런데 첫번째 인자는 Animal 클래스네요.
정말 놀랍죠? 생성자에서는 $super로 부모의 메서드도 호출할 수 있습니다.
이 예제에서는 명시적으로 부모의 생성자를 호출하는군요.
자 한번 인스턴스를 생성해 봅시다.
[code]var ringneck = new Snake(“Ringneck”, “hissssssss”);
ringneck.speak();
// -> “Ringneck says : hissssssssss!”
var rattlesnake = new Snake(“Rattler”);
rattlesnake.speak();
//-> “Rattler says: hissssssssss!”[/code]
자 굳이 설명 안해도 다들 잘 아실꺼 같네요.
그렇다면 이번에는 Prototype 에서 제공하는 Enumarable 이라는 클래스를 상속받아 하나 만들어 보도록 하죠.
[code]var AnimalPen = Class.create(Enumerable, {
initialize: function() {
// 인자 리스트를 가져옵니다. 예측하기 힘든 다수의 인자를 받을때 사용합니다.
var args = $A(arguments);
// 인자 모두가 Animal 클래스의 인스턴스인지 확인합니다.
if (!args.all( function(arg) { return arg instanceof Animal }))
// 거짓이라면 오류 발생
throw “Only animals in here!”
// 모든 인자가 Animal 인스턴스라면 animals 프로퍼티에 담습니다.
this.animals = args;
},
// Enumerable 클래스의 _each 메서드를 재구현
_each: function(iterator) {
// animals가 Array기 때문에 해당 _each를 수행하도록 메서드 변경
return this.animals._each(iterator);
}
});[/code]
실행을 해볼까요?
[code]var snakePen = new AnimalPen(ringneck, rattlesnake);
snakePen.invoke(‘speak’);
//-> “Ringneck says: hissssssssss!”
//-> “Rattler says: hissssssssss!”[/code]
invoke는 Array같은 반복 처리 가능한 객체의 하나하나의 원소에 해당 메서드를 수행하는 메서드입니다.
추후 Enumerable 클래스를 공부할때 다시한번 이야기 하도록 하죠.
2. addMethods
클래스를 생성할때 Class.create를 사용할 것입니다. 선언하는 당시에 create의 인자로 메서드 리스트를 입력하지만 동작 중간에 메서드가 추가되어야 하거나 혹은 변경 되어야 한다면 어떻게 할까요?
이 addMethods를 이용하여 클래스를 실행중간에 메서드를 추가 또는 변경 할 수 있습니다.
신기하게도 그럼 해당 클래스를 이용하여 생성한 모든 인스턴스들이 변경된 사항을 적용 받습니다.
위에서 사용했던 소스를 다시 한번 가져와서 사용해 보죠.
[code]var Animal = Class.create({
initialize: function(name, sound) {
this.name = name;
this.sound = sound;
},
speak: function() {
alert(this.name + ” says: ” + this.sound + “!”);
}
});
// Animal 상속 받음
var Snake = Class.create(Animal, {
initialize: function($super, name) {
$super(name, ‘hissssssssss’);
}
});
var ringneck = new Snake(“Ringneck”, “hissssssss”);
ringneck.speak();
//-> “Ringneck says: hissssssss!”[/code]
자 여기서 Snake 클래스에 speak 메서드를 추가해 보겠습니다.
[code]Snake.addMethods({
// 실행시에 부모 지시자를 인자로 넣음
speak: function($super) {
// 부모(Animal) 메서드 호출
$super();
alert(“You should probably run. He looks really mad.”);
}
});[/code]
한번 실행해 보죠.
[code]ringneck.speak();
//-> “Ringneck says: hissssssss!”
//-> “You should probably run. He looks really mad.”[/code]
addMethod가 수행되기 이전에 생성된 인스턴스 객체들도 다 적용이 되어있는걸 확인할 수 있습니다.
이번에는 부모 클래스인 Animal에 이미 존재하고 있는 speak() 메서드를 재정의 해보겠습니다.
[code]Animal.addMethods({
speak: function() {
alert(this.name + ‘snarls: ‘ + this.sound + ‘!’);
}
});[/code]
says를 snarls로 바꾸어 보았습니다. 실행해 볼까요?
[code]ringneck.speak();
//-> “Ringneck snarls: hissssssss!”
//-> “You should probably run. He looks really mad.”[/code]
바뀌었군요! 이것을 잘 활용하면 굉장히 재밌는걸 만들 수 있을 것 같네요.
갑자기 게임을 만들고 싶어지네요. 무기를 업그레이드 할때마다 shoot() 메서드를 재정의하면 될테니깐요.