JavaScript에서의 클래스.

요즘에는 재미삼아(?) + 공부겸 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()라는 메서드를 뺄셈으로 사용한다면 올바르지 않은 결과가 나올 수 있다!)

클래스에 대한 설명은 우선 이것으로 마치자. 생각이 나는데로 추가 내용을 다시 정리해야겠다.

크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 장현준

2007/08/06 22:54 2007/08/06 22:54
, ,
Response
No Trackback , 2 Comments
RSS :
http://b4you.net/blog/rss/response/126

Prototype:: Class.create() 분석

Prototype 소스를 분석하다 보니 아래와 같은 소스가 나왔다.

var Class = {
	create: function() {
		return function() {
			this.initialize.apply(this, arguments);
		}
	}
}


코드는 전체적으로 간단한데, 여기서 사용되는 apply가 무슨 역할을 하는 함수일까?

일반적으로, 함수를 호출할 때

function f1(arg1, arg2) {
	f2(arg1, arg2);
}

function f2() {
	alert(arguments.length);
}


와 같이 호출한다. 이 때, 만약 f1의 인자가 가변이라면 어떨까?

function f1(arg1, arg2, arg3) {
	f2(arguments);
}

function f2() {
	alert(arguments.length);
}


라고 하면?
f2에 첫번째 인자가 Array형으로 들어가지는 바람에, length의 값은 3이된다.

그래서 이것을 해결하고자! (물론 이것만 해결하는게 아니라, this까지 넘겨주어서 타겟 메소드가 현재 객체의 메소드같이 수행되는 역할도 한다)

function f1(arg1, arg2, arg3) {
	f2.apply(this, arguments);
}

function f2() {
	alert(arguments.length);
}


라고 표현을 한다. 이러면 arguments라는 배열이 f2를 호출할 때 각각의 인자로 쪼개져서 들어간다.

이렇게 하면, 해당 객체의 initialize를 호출해서 초기화 하는 과정이 끝났다.
그렇다면 어떻게 생성자가 호출이 될까?

그 비밀은 Class 객체 안의 create 메소드를 보면 정답이 있다.

create 메소드는 function() 을 리턴하는데, 이는 함수의 포인터를 리턴한다.
이 함수의 포인터는 "생성자를 호출해주는 함수" 를 가리키고 있다. 따라서 이 함수의 포인터를 실행하면 생성자가 호출이 되는것이다.
이 함수의 포인터는 어디에 있을까?
바로 여기다!

var PeriodicalExecuter = Class.create();


이렇게 하면 PeriodicalExecuter변수에는 생성자를 실행해주는 함수의 포인터가 담겨진다.
즉, 결론은 create를 실행하는 시점에서는 생성자가 호출되지 않으며, 나중에 호출되는것이다!

그렇다면 이 함수의 포인터는 언제 실행할까? 바로

var myInstance = new PeriodicalExecuter();


를 호출 할 때 실행되는것이다. 이때 new는 익히 잘 알고 있듯 PeriodicalExecuter()를 실행한 결과를 메모리에 할당시킨 후 그 결과를 myInstance에 넣어주는 역할을 한다.
따라서 결과적으로 이 때 initialize가 호출이 되는 것이다.

소스를 분석하면서 느낀것이지만, 한줄 한줄의 의미가.. 신기하다.ㅡㅅㅡ

크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 장현준

2007/07/20 11:38 2007/07/20 11:38
, ,
Response
No Trackback , No Comment
RSS :
http://b4you.net/blog/rss/response/123

클래스, 객체, 인스턴스에 관한 고찰

Prototype를 분석하다 보니.. 이런 문제가 발생했다.

지금까지 알고 있는 사실로는..

객체는 붕어
클래스는 붕어빵틀
인스턴스는 붕어빵

으로 정의를 하였는데,
Prototype에서 사용되는 것들에 적용해보려고 하니.. 많이 헷갈렸다.

추측 끝에,

객체는 new를 사용하지 않고 쓰는 것
클래스는 new를 사용하고 쓰는 것
인스턴스는 객체나 클래스를 가리키는 것

라고 정의를 내렸는데 아직까지는 모순이 있는거 같진 않다.
너무 당연한 정의인데.. 괜히 머리를 쥐어 뜯었나 ㅠ_ㅠ

=====
결론은 JS에서는 그냥 다 객체이기도 하나보다;

크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 장현준

2007/07/20 11:26 2007/07/20 11:26
, , , ,
Response
No Trackback , No Comment
RSS :
http://b4you.net/blog/rss/response/122

AJAX Prototype Study Group 첫 모임

몇일전에 모이기로 했었던 ALC 스터디 그룹이 처음으로 모였다.

마침 전날에 핸드폰이 사망하여 주시느라 (전파가 안뜬다) 그날 A/S 센터에 갔다 오느라 2시반까지 가야되는 모임에 늦었다.

이것저것 설명을 듣고 prototype 라는것의 소스를 보았는데..
음 뭐랄까? php를 보는듯한 구조..

자바스크립트가 예전부터 틀이 없었다는건 알았지만 php와 비슷하다는 점에서 (js가 심할지도 바로 함수를 대입해서 생성해버리니.. -ㅁ-) 그동안 두리뭉실 하던게 좀 잡히는것같았다.

예전에도 JS는 객체지향 언어라고 생각하는지라 그쪽에서는 별로 감흥(?)을 못느꼈다.
(일반적으로 우리는 JS를 절차지향 언어로 사용하고 있다. 하지만 JS는 객체같이도 사용할 수 있다.)
JS안에서 충분히 객체를 만들어서 사용하였고, Prototype에서는 그 객체들을 사용하도록 만들었는데..
JS에서 원래 상속관계가 없다. 그래서 prototype에서는 extend()라는 메소드를 제공하는데..
소스를 보면 매우 간단하고, 한눈에 의미가 파악이 되지만, 저런식으로 사용해서 상속관계를 준다는 점이 신기하였다.

전체적으로 느낀점은.. 소스는 어렵지 않은데.. "저런식으로 프레임워크를 제공할 수 있구나"라는것? 좀 더 공부한 뒤에 다른 언어에서 이렇게 다른 방법으로 접근 할 수 있는 방법을 연구해보는것도 재미있을 것 같다.

앞으로 매 주 모여서 공부를 할 예정인데, 이번 공부가 끝나면 그동안 귀찮아서 못했던 AJAX에 대해 고수가 될 것 같은 느낌이다.ㅋㅋ

P.S. 첫 모임이래서 사진이라도 있어야 될 것 같은 느낌이지만.. 처음이라 그런거 없다;;ㅋㅋ

크리에이티브 커먼즈 라이센스
Creative Commons License

Posted by 장현준

2007/07/15 13:32 2007/07/15 13:32
, , ,
Response
No Trackback , No Comment
RSS :
http://b4you.net/blog/rss/response/120


블로그 이미지

빗소리를 먹는 사람.

- 장현준

Notices

Archives

Authors

  1. 장현준

Recent Trackbacks

Calendar

«   2017/11   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

Site Stats

Total hits:
1973434
Today:
5258
Yesterday:
5236