요즘에는 재미삼아(?) + 공부겸 Prototype를 연구하고 있다.
(연구실 프로젝트 하다가 심심하면 Prototype 보는중 -ㅅ-)
그러던 도중 JS에도 클래스가 있다는것을 깨달았다.
옛날 언제였을까? 몇년전 아주 잠깐!(약 30초?ㅋ) CSS 부분에 (내가 개념이 덜 장착된 시기라, JS였을 확률이 높지만) 클래스가 있는것을 보았다.
CSS에 클래스?
왜 쌩뚱맞냐고 말을 한다면 할말이 없다. 그때 그렇게 봤을뿐. (그 소스를 보면서 잠깐 분석했는데, 그때 내 실력으로는 결국에는 CSS라고 생각했다)
근데 신기하다. 이 클래스는 상속을 하는데.. 이벤트를 처리하고 있었다.
지금 생각해보면 말이 안되지만 (이런걸로 혼돈하는건 내가 이쪽으로는 아는게 없기 때문이다. 실제로 가능한걸 지금 말이 안된다고 하는 것일지도..) 그때 확실히 신기하다고 느꼈다.
웹상에서 구할 수 있는 소스 중에서.. 가장 난해한 소스라고 할까?
지금은 못 구하는 소스(그 당시에 아무나 접근 못하는 일급 비밀의 사이트 정도? 였으니ㅋ - 지금의 airensoft 홈페이지같이 - 지금은 구할 수 없다.)라서 다시 보면 좀 낫겠지만서도..
각설하고 본론으로 들어가자.
JS는 클래스가 있을까?
현재까지 내가 봐온 결과로는 클래스는 없다. 클래스 개념은 있다. 아래에서 설명할꺼다.
단지 클래스가 없다는 말은 클래스를 위한 keyword가 없다는거니 이해하시길.
(JS에서는 함수가 함수요, 클래스요, 객체이다.)
Java를 해온 사람이 흔히 실수하는게 JS는 Java랑 이름이 비슷하니깐 실제로도 비슷하게 쓰면 되겠지 라는 생각이다.
실제로 Math 클래스에서 제공하는 메서드같은것들은 거의 다 비슷하다. Integer.parseInt()도 사용할 수 있다.
그런데 뭐가 다를까?
JS는 좋은말로 유연하다. 나쁜말로는 소스가 변태적이다.-_-;;;;;;
뭐.. 우선 예를 보면 안다.
일반적으로 JS는 아래와 같이 쓴다.
function myFunc(a, b)
{
return a + b;
}
alert(myFunc(1, 2));
이걸 좀 색다르게(?)~
var myFunc = function(a, b)
{
return a + b;
}
alert(myFunc(1, 2));
여기까진 멀쩡하다. 왜냐고? myFunc는 함수의 포인터니까. 함수를 가리키고 있는 상태기 때문에.
그럼 이걸 new 형태로 사용할 수 있는 클래스로 만들어보자. 물론 처음부터 머리아파지라고 Prototype를 참고해서.ㅋㅋ
var FuncObject = Class.create();
FuncObject.prototype = {
initialize: function()
{
},
myFunc: function(a, b)
{
return a + b;
}
}
var myObject = new FuncObject();
alert(myObject.myFunc(1, 2));
Prototype에서 사용되는 생성자(?)를 이용한 객체다. (아직 이런 객체는 손에 익숙하지가 않아서 이런 초간단 예제를 만드는것도 힘들다.)
왜 굳이 저렇게 복잡한 과정을 거칠까?
그건 Prototype:: Class.create() 분석(http://b4you.net/tt/123) 을 보시라~
그러면 Prototype에 나온거 말고.. 우리 손으로 클래스 구조를 만들 순 없을까?
이런 생각을 했다면 당신은 나같은 타입.ㅋㅋㅋ (욕으로 들리십니까?) 아래와 같이 해보자.
var myObject = new Object();myObject.myFunc = function(a, b) { return a + b; }
alert(myObject.myFunc(1, 2));
우와~ Class.create()를 사용하지 않고도 클래스 흉내를 냈잖아?!
뭐.. 사실 그렇다. JS에서 최고의 장점이자 단점인건 이런 애매 모호한 구문들이다.
익숙해지면야 편하겠지만.. 익숙해지기 전까지는 손발이 고생하겠군.
자 그럼 다음 코드는 어떨까.
var myObject = new Object();myObject = { myFunc: function(a, b) { return a + b; } }
alert(myObject.myFunc(1, 2));
바로 이전의 소스와 동일하다. 아니 동일하지 않다! 바로 위에서 설명한 예제는 기존의 객체의 내용을 싸그리 날려버린다.
새집을 지어서 그안에 할당한다는 말씀.
("{}"를 이용하여 아예 객체를 대입 시킨거니 당연하다. 하지만 헷갈릴지도 모르기 때문에 설명한다.)
이해가 안간다면..
var a; a = 10;a = 20;
와 같이 a를 선언한 뒤 10을 넣었다 다시 20을 넣는 꼴이다.
그러면 원래대로 돌아가서.. {}를 사용하지 않고 단순히 "Object.method()" 꼴로 선언한다면 어떨까?
기존의 객체에 method가 추가되는 것 같이 동작한다.
(다시한번 밝히지만 알고나면 정말 당연한 사실이다.)
좀 더 위로 올라가보자. 간단하게 저렇게 하면 되는데.. 왜 JS에서.. 특히 Prototype.js안에서는
prototype이란 property에 method를 할당할까?
prototype는 클래스에 존재하는 method를 오버라이딩 할 수 있는 기회를 주기 때문이다.
JS에서 제공하는 기본 클래스 조차도, prototype을 이용하여 오버라이딩 할 수 있다.
이는 C++에서 연산자 오버라이딩의 강력함을 생각하면 될 것이다.
(여기서.. 연산자 오버로딩이 될 수도 있고 오버라이딩이 될 수도 있다. 필자는 기존에 있는 연산자를
재정의 하기 때문에 오버라이딩이라고 했다.)
prototype property를 이용하면 어떤게 가능한지 보자.
Object.prototype.toString = function()
{
return "난 이제 더이상 Object.toString()이 아냐.";
}
var a = new Object();
alert(a);
alert와 같이 string형이 필요한 곳에서는 자동으로 toString()이 실행된다.
위와 같이 오버라이딩이 가능한데...
여기서 드는 의문. 그렇다면 반드시 prototype property를 이용해야 할까?
Object.toString = function()
{
return "Object.toString()를 벗어날 수 없어 ㅠㅠ";
}
var a = new Object();
alert(a);
기존의 코드가 실행된다. 즉 prototype property는 C++에서의 virtual로 생각하면 된다.
(개념이 같다는게 아니다! 둘이 같은 역할을 하는것도 아니다! 다만 상위 클래스에서 정의한 메서드를 하위 클래스에서 재정의 한 데이터로 사용할 수 있게 해준다는 의미에서 말하는거다. 둘이 절대! 동일하지는 않다.)
처음으로 돌아가보자.
JS에서 클래스를 사용하는 방법은 위에서 설명한 것과 같이 하면 된다.
prototype.js에서 클래스의 메서드를 정의할 때 prototype라는 property를 이용하는건 오버라이딩을 위해서이다.
위에서 보았듯이 일반 클래스들은 다른 방법으로도 가능하다. 다만 native 메서드들 때문인거다.
대충 클래스에 대한 설명을 하였으니 이번엔 상속으로 가보자.
현재까지 본 결과로는 JS에서는 상속개념이 없다. 다만 확장의 개념만이 있을 뿐.
하지만 뭐.. 상속 같이 사용할 수 있으니 상속이라고 해두자.
여러개의 객체로 부터 상속을 받을 수 있으니 다중 상속도 가능하다.
(단지 오버라이딩 된다고 생각하면 된다)
객체별로 정리되서 메서드가 정의되는게 아니기 때문에, 매우 단순하다.
그냥 property별로 새 객체에 복사하는거다.
깔끔하게 걍 소스를 보자.
Object.extend = function(destination, source)
{
for (var property in source)
{
destination[property] = source[property];
}
return destination;
}
source에 있는 모든 property들을 destination으로 복사한다. for...in 구문을 사용하였으니 source에 있는 모든 property들이
적나라 하게 destination으로 복사 될 것이다.
이런식으로 복사가 되기 때문에, C++과 같이 죽음의 다이아몬드 문제같은건 없을꺼 같다.
(다만 내부 메서드를 실행하는 구조에서는 문제가 생길 가능성이 있다. A라는 객체에서는 a()라는 메서드를 덧셈으로 사용하는데 B라는 객채에서 a()라는 메서드를 뺄셈으로 사용한다면 올바르지 않은 결과가 나올 수 있다!)
클래스에 대한 설명은 우선 이것으로 마치자. 생각이 나는데로 추가 내용을 다시 정리해야겠다.
Posted by 장현준


