prototype - 해당되는 글 7건
오랜만에 글을 올리는 것 같군요.. ^^

한창 더울때 post를 시작한 것 같은데 벌써 날이 제법 찹니다.. 감기조심하세요.

오늘은 제목처럼 자바스크립트에서 주로 사용하는 객체 생성방식에 대해서 알아보려고 합니다. 또한 생성할시에 필요한 메모리도 한번 체크해보도록 합시다.

자바스크립트에서는 클래스라 불리우는 객체를 두가지 방식으로 선언할 수 있습니다.
첫번째는 function으로만 선언하는 방식이고, 다른하나는 function과 prorotype을 이용하여 선언하는 방식이 있습니다.

function Foo(){
   this.name = "foo";

   this.getName = function(){
      alert(this.name);
   };

   this.setName = function(name){
       this.name = name;
    };

    this.act = function(){
 
    }
}


보시다 시피 위의 소스는 function으로 Foo객체(클래스)를 생성한 모습입니다. 일반적으로 위의 방식을 주로 사용합니다.

그리고 저는 후자의 방식을 사용하는데요... 소스는 아래와 같습니다.

function Foo(){
   this.name = "foo";
}

Foo.prototype = {
   getName : function(){
      alert(this.name);
   },
 
   setName : function(name){
      this.name = name;
   },
 
   act : function(){
 
   }
}


먼저 function으로 필요한 부분은 멤버로 넣고 각 인스턴스마다 공통으로 사용될 것같은 놈들만 골라서 prototype으로 처리했습니다.
(여기서는 그냥 가상이므로 getName과 setName, act메소드에 대한 내용은 언급하지 않습니다)

그리고 아래의 소스를 돌렸습니다...위의 두가지 방식의 소스 모두...

for(var i=0; i<10000; i++){
    eval("f_"+i+" = new Foo();");
}


결과는 어떻게 될거라 생각하나요??


우선 아무것도 없을때의 화면입니다.
사용자 삽입 이미지


두번째는 function으로 선언하고 위의 소스를 돌렸을때의 화면입니다.

사용자 삽입 이미지


다음은 prototype과 function을 함께 사용했을때의 화면입니다.

사용자 삽입 이미지


예상하셨나요??

제가 위에도 잠시 언급했지만.. function으로 생성된 인스턴스는 해당 클래스에 존재하는 모든 멤버를 모두 설정하여 생성됩니다.

허나, 필요한 멤버는 function으로.. 각 인스턴스마다 공유될 멤버 메소드같은 경우는 prototype으로 선언하면 function으로 생성된 인스턴스는 function의 멤버인 하나만 설정하여 생성되고 나머지 필요한 것은 Foo로부터 찾아서 사용하게 됩니다.

많은 분들이 function형태의 클래스 생성과 prototype으로 생성한 것과의 차이점을 간과하시는 듯하여 작성한 post가 되겠습니다.

갑자기 추워진듯 한데 감기조심하시구요. 다음 포스트때 다시 뵙도록 하겠습니다.

posted by blankus
Javascript_power  |  2007/11/01 17:00

어제도 무척이나 덥더니, 오늘도 매한가지군요;; 집에 에어컨이 없는관계로 너무나 더운 초여름을 맞이하고 있습니다. 어제 새벽엔 개인적인 사건사고가 끊이지 않아~ 피곤한 밤이기도 했습니다. ㅎㅎ

오늘은 저번 강좌에서도 밝혔듯이 bind 개념의 후속타인 extend에 대해서 알아볼까 합니다. PJS에서 extend의 개념은 두가지로 쓰이고 있습니다. 하나는 확장이며, 다른하나는 상속입니다. 어찌보면 두개의 개념이 같을 수도 있습니다. 같은 행동을 취하지만 개념은 다르다라고 말씀드리는게 더 정확하다고 말하고 싶군요.

Object.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}

먼저 extend의 소스부터 보시죠. 감이 오시나요? 그렇습니다. 마냥 읽어서 shallow copy를 하고 있습니다.
Shallow copy는 일명 '얕은 복사'입니다. 얕은 복사는 대상 오브젝트를 참조하는 참조변수로부터 복사되어지는 변수만을 가지는 복사를 말합니다. 이해가 잘 안가신다구요? 그렇다면 저~ 뒷쪽에 변수의 scope를 설명하는
부분을 참고하시면 이해가실겁니다. shallow copy의 반대는 뭘까요? 그렇습니다 deep copy(깊은 복사)입니다. 너무 쉽죠? 깊은복사는 말그대로 객체를 복사할때 참조변수가 주소값만을 가지는게 아니라 새로운 객체자체를 가지는것을 말합니다. 이쯤하면 shallow copy와 deep copy가 구분되어지리라 생각합니다.

음, 제가 말하고 싶은건 'extend는 shallow copy다' 라는 것입니다. ㅎㅎ

그럼 다시 extend의 목적인 두가지를 알아보겠습니다.

첫번째, '확장'입니다.
확장이란 말그대로 무언가를 넓히는 것입니다. 그게 개념이든  메소드이든 말이죠. 그럼 확장의 extend를 언제
사용할까요? 바로 객체의 메소드 및 프로퍼티를 추가할 때 입니다. 이해가시죠? 어느 객체에 필요한 프로퍼티와 메소드들을 추가하여 없던 기능들을 넣음으로써 확장하는 것입니다. 이것을 PJS에서는 아래와 같이 사용하고
있습니다.

Object.extend(Hash, {..toQueryString : function(){}..toJSON : function(){}...}) 

처음에 Hash를 생성할 당시에는 toQueryString이란 메소드가 없다가 확장을 통해 메소드를 추가하는
모습입니다.

두번째의 개념은 '상속'입니다.
상속이란 확장의 확장편이라고 해야할까요? ㅎㅎ 그냥 의미그대로 해석해보겠습니다. 상속이란 위에서 그냥
내려받아 내것처럼 사용하는 것을 말합니다. 쉽게 저는 빵을 못만들지만 아버지가 빵을 만드는 방법을
알고있다면, 자식인 제가 아버지의 재능을 상속받아 저또한 빵을 만들 수 있다는 이야기 입니다.
이해가 가시는지요. 그럼 PJS에서는 어떻게 상속의 개념을 설명하는지 코드를 보겠습니다.

Object.extend(Hash.prototype, Enumerable)

아주 심플하군요. 소스설명은 Hash 오브젝트가 Enumerable이 사용하는 메소드들을 상속받아서 Hash에서도
사용할 수 있다는 이야기 입니다. 상속이란 이런 개념인거죠. 내가 못하는거 상속받으면 상속해준 것의 행동을 할 수 있다. 이해가 되시죠? 그렇습니다. 그렇게 어려운 개념이 아닙니다.


오늘은 여기까지 작성토록하고 다음 강좌에서 다시 만나겠습니다.

posted by blankus

Ajax  |  2007/06/17 13:32

이번 강좌는 PJS의 11장에 해당되는 Function에 대해서 알아볼까 합니다.
PJS의 중간에 다른 챕터들도 있지만 일단 다른 소스를 분석하려면 필수 개념들이 몇개 필요하다고 전 강좌에서 말씀드렸습니다. 그중에 하나인 bind의 개념을 소개하고자 챕터 11인 Function을 먼저 이야기하고자 합니다.

소스의 길이가 그리 길지 않으므로 원소스를 일단 보도록 하겠습니다.

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}


이것과 비슷하게 생긴 bindAsEventListener 라는 것도 있지만 대동소이하므로 아랫부분에서 간단하게
알아보겠습니다.

뭐 이렇게 생긴놈입니다. ㅎㅎ 이해가 잘 안되신다구요? 잠시 기다려주세요. 일단 문제부터 풀어봐야 합니다. 문제는 아래와 같습니다.

window.name = "window object"

var foo = {
 name : "foo object",
 scope : function(){
  alert(this.name);
 }
}

function fx(f){
 f();
}

var f = foo.scope.bind(foo);

fx(f); ----------------------------- ①
fx(foo.scope); -------------------- ②

1번의 답과 2번의 답. 예상되시는지요? 만약 정확하게 맞추시고 이유도 아신다면, bind의 개념은 이미
정립되었다고 봐도 무방합니다.

1번의 답은 "foo object"이고 2번의 답은 "window object"입니다. 답을 맞추셨나요? 그렇다면 이유도 설명할 수 있나요? 있다면~ 패스~! 없다면 아래의 글을 봐주세요. ㅎㅎ

var foo 는 name이라는 멤버 프로퍼티를 가지고 있으며 scope라는 멤버 메소드를 가지고 있습니다. 멤버라는 것은 객체에 속한 오브젝트라는 뜻입니다. 다들 아시죠? scope라는 멤버 메소드는 this.name이라는 값을 출력하는 행동을 담당하고 있군요.

function fx()는 인자로 함수를 받아 실행하는 단순구조로 되어있습니다. 아마 처음보신 syntax일 수도 있겠습니다. 처음보신 분은 "저런게 가능하다"정도로만 이해주시면 될듯합니다. 저런 syntax가 가능한 이유는
자바스크립트에서는 인자로 오브젝트를 넘길 수 있으며, 자바스크립트에서 함수또한 함수 오브젝트 이므로
함수도 인자로 넘길 수 있는 것입니다
. 계속가죠~

var f = foo.scope.bind(foo); 라고 되어 있는데, 일단 뭔가를 하는놈인지 알 수 없지만, 그렇다 치고
넘어가시고 다음줄을 봐주세요.
일단 2번부터 살펴보면, 위에서 설명한것 처럼 객체의 멤버 메소드인 foo.scope를 함수의 인자로 넘기고
있습니다. 여기서 부터 함정이 있는데요, 그것은 다음과 같습니다.

fx(foo.scope)의 답이 "foo  object"라고 생각하신 분은,
  1. 함수가 실행된다? 함수는 this.name을 출력한다.
  2. 함수안에 name이라는 놈이 있다? 그놈을 출력한다
  3. 그리하여 답은 "foo object"가 출력된다.


제가 예상한게 맞았나요? 하지만 위에서 말씀드렸듯이 답은 아닙니다. 정확한 답은 이 한줄로 표현할 수
있을것 같습니다.
"함수의 scope는 구현시점이 아닌 실행시점의 scope를 가진다" 무슨말이냐면,

fx(foo.scope)를 수학에서의 대입법을 통해 알아보면, fx(alert(this.name)) 이렇게 됩니다.
즉, 단순한 alert(this.name)을 출력하는데, 제가 묻겠습니다. this.name은 누구입니까? "foo object"입니까? 아닙니다. 바로 "window object"입니다. 이해가 잘 안가시는 분은 제가 밑에서 강의한 "변수"부분을 다시 읽어주시기 바랍니다. 그 글을 읽으시면 충분히 이해하시리라 믿습니다. 그래도 안읽고 이것만 보시는 분을 위해
간략하게 설명하면 "변수의 scope는 전역변수보다 지역변수가 우선하며, 지역변수가 존재하지 않을시 그위의
객체의 변수(더이상 없으면 전역변수)를 참조한다"
라고 말할 수 있습니다.

그렇다면 어떻게 하면 이런 scope의 변경없이 원하는 답을 얻도록 함수를 실행할 수 있을까요? 그렇습니다.
답은 bind 메소드 입니다. bind의 형태를 자세히 보시면 bind의 인자로써 참조객체를 넘기고 있습니다.

var f = foo.scope.bind(foo);

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

그리고 bind를 설명드리면 var __method = this는 bind가 참조하고 있는 메소드가 되겠습니다. 위에서는
var __method = scope함수객체 가 되겠군요. 그리고 args = $A(arguments)라고 되어있습니다. $A메소드는
전 강좌에서 알아봤듯이 인자로 넘어온 객체를 배열객체로 만들어 리턴합니다. 여기서는 foo 객체자체가 배열로 들어오겠군요. 왜냐구요? .bind(foo)로 넘겼잖아요 ^^
object = args.shift()는 args가 가진 배열의 요소중 첫번째것을 object에 담고 나머지는 다시 args에
셋팅하게 됩니다. 예를 들면, args가 3개의 배열요소[1,2,3]을 가지고 있다고 합시다 그럼 object = [1]이
되고, args는 [2,3]이 됩니다. 이해하셨는지요? shift라는 메소드가 그런 역할을 하고 있습니다.
그럼 여기서는 어떻게 되나요? arguments로 넘어온 것은 foo객체 하나이므로 args의 크기는 1인 배열이
되겠군요, 그리고 바로 shift로 잘라내고 나니 object에는 foo객체가, args에는 빈배열
이 되어버렸군요. ㅎㅎ

여기까지는 쉽게 이해하셨지요? 구분을 좀더 보시면 return을 함수로 하고 있군요. 이런 구문은 Class.create() 에서 다뤄봐서 알겠지요? ㅎㅎ 그럼 계속 설명을 하겠습니다. 여기서 오해가 좀 생길 수 있으니 정신을
집중하세요 ^^

function() {
    return __method.apply(object, args.concat($A(arguments)));
  }


위 소스가 바로 리턴되는 함수의 원모습인데, 여기서 보시면, __method는 scope 인것을 이미 확인했고, object는 foo객체가 되고 args에는 아무것도 들어 있지 않습니다. 빈 배열이죠. 그다음 논란이 되는
구문인 args.concat($A(arguments)) 입니다.

많은 사람들이 왜 args에 다시 $A(arguments)를 다시 concat(두개의 배열을 합쳐 새로운배열을 리턴)하느냐고 묻습니다. 하지만 단호하게 대답하겠습니다. 위 구문에서의 arguments와 args = $A(arguemnts)에서의 arguments와는 다릅니다. 왜일까요? 네, 그렇습니다.
바로 return __method.apply(object, args.concat($A(arguments))) 가 정의된, 구현된 시점에서의  arguments가 아닌 실행시점의 arguments이기때문에 다른 것 입니다. 이해가 되셨는지요?

좀더 부연설명을 드리면, arguments는 실행시점에서 함수가 인자로 넘어온 값을 처리하는 native 컬렉션
오브젝트입니다. 그러므로 var __method = this, args = $A(arguments), object = args.shift(); 여기에서
정의된 $A(arguments)와는 다른것입니다
.  여기까지 이해하셨다면 bind의 개념은 이미 다 정립된 것입니다.

bind를 말로 풀어내면, "함수가 다른 함수안에서 실행될때 자기자신의 scope가 없어지는 것(멤버 프로퍼티에 접근하는 this 키워드 사용시)을 방지하기 위해 실행될 함수가 가지는 객체의 모든 것을 인자로 넘겨 실행시에 scope가 변경되지 않도록 하기 위함" 이라고 정의할 수 있겠습니다.

약간은 어려운 개념일 수도, 이해가 잘 안가실 수도 있는데 수학에서의 대입법을 통해 직접 확인해 보심이
좋을 것 같습니다. 백문이 불여일타 라고 하지않습니까? ㅎ 직접 해보시면서 제글을 보시면 더 확실히 이해가 가실거라 생각합니다.

위에서 잠시 언급한 bindAsEventListener의 모습은

Function.prototype.bindAsEventListener = function(object) {
  var __method = this, args = $A(arguments), object = args.shift();
  return function(event) {
    return __method.apply(object, [event || window.event].concat(args));
  }
}


이렇게 생겼는데, bind와 거의 유사하고 다른점이 하나있다면, apply할때 event객체를 args배열의 앞쪽에 먼저 붙여준다는 것외엔 다를것이 없습니다. 따로 설명은 드리지 않겠습니다.

이상으로 PJS의 11장에 해당하는 Function에 대해서 알아봤습니다. 자바스크립트의 syntax에 약하신분은 좀
버거운 내용이 아니였나 싶은데, 몇번을 읽고 또 읽고, 직접 실행을 해보면서 하시면 충분히 될 것으로
생각합니다.  그럼 다음 강좌때 뵙기로 하고 오늘은 여기까지 하겠습니다.

posted by blankus

Ajax  |  2007/06/16 20:06

오랜만에 다시 포스트 하는것 같습니다. 그동안 개인적으로 좀 바쁜것이 있어서 블로그에 글을 포스트하지
못했습니다 ㅠㅠ 오늘은 회사의 당직이라, 편안히 글을 써볼까 합니다. ㅎㅎ

제목에서와 같이 오늘은 Class  입니다. 이부분에 대해서는 크게 쓸것은 없지만 개념적인 부분은 집고
넘어가겠습니다. 우리는 자바스크립트로 클래스를 생성할때 어떻게 하고 있습니까? 보통 이렇게 하겠지요?

var Test = function(){
  ..........
}
var test = new Test();


여기서 Test는 객체이고 test는 객체의 인스턴스가 되겠죠. 물론 Test 객체를 정의하는 방법은 이것말고도 많습니다만 일반적으로 저렇게 많이들 하시죠. 구조적으로 하자면 Test객체를 구현하는 "구현부"와 new 를 통한
객체생성을 하는 "생성부"로 나눌 수 있겠습니다.

그러면 PJS에서는 어떻게 쓸까요? 다음 코드를 보시죠.

var Animal = Class.create();

Animal.prototype = {
  .............
}

var snake = new Animal("b_snake","12");


이런식으로 사용하며 var Animal = Class.create() 부분을 "선언부", Animal.prototype = {...}을 "구현부", new Animal 부분이 "생성부"로 나눌 수 있습니다.

그럼 1번방식과 2번의 PJS방식의 차이점이 무엇일까요? 그렇습니다. 선언부입니다.
PJS에는 명시적으로 클래스의 생성에 대해서 "선언부"를 가지며, 개념적인 의미를 부여합니다. 즉, "나는
클래스로서 생성되었습니다, 그러니 저를 클래스로 인정해주시고 부디 다음부터는 new를 통해 생성해서
인스턴스를 만들어 사용해 주세요"
라고 말이죠. 혹자는 이렇게 이야기를 합니다.
왜 불편하게 한줄을 더 추가하면서 까지 저렇게 쓸까요? 단순히 개념적인 것이 전부인가요?

아닙니다. 물론 내부적으로 개념적인 부분이 사용되는 것은 맞습니다. 하지만 그것이 다는 아닙니다.
PJS의 원 소스를 보시죠.

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


create가 실행되면 함수를 리턴합니다. 어떤 함수냐구요?  바로 아래의 함수입니다.

function() {
      this.initialize.apply(this, arguments);
}


쉽게 표현하자면, var k = Class.create() 라고 하면,
var k = function(){this.initialize.apply(this, arguments);} 이런식으로 되겠죠. 쉽게 수학에서의 대입을
생각하시면 됩니다. 그리고 우리가 k 객체를 생성하기 위해 new k()를 하는 순간 코드는 다음과 같이 변합니다.

new function(){this.initialize.apply(this, arguments);}()

위에서  this는 바로  new function, 자기자신을 나타냅니다. this.initialize 또한 new function안의
멤버이기때문에 new function에서 가지고 있어야할 객체입니다. apply는 native 메소드로 뭔가를 실행시켜주는 역할을 합니다. 인수로는 자기자신과 실행할 메소드의 arguments를 받구요. 비슷한 메소드로는 call이
있습니다. call은 이렇게 사용합니다. call(obj, [param1, param2, param3....])
다시 본문으로 돌아가서, 위 구문을 말로써 해석하면 이렇게 됩니다.

"새로 정의된 함수(new function)의 initialize 메소드를 실행(apply)하세요,
인자는 arguments로 넘겨드립니다"

여기서 우리는 알게됩니다. Class.create()로 생성한 클래스는 반드시 멤버메소드로 initialize가 존재해야
한다는 것을,  JS에는 존재하지 않는 "생성자"의 개념을 사용하고 있다는것을.


var Animal = Class.create();

Animal.prototype = {
  initialize : function(name, age){
    this.name = name;
    this.age = age;
  },
  getName : function(){
    return this.name;
  }
}

var snake = new Animal('dog', '5');

위의 소스는 다시 분석하지 않아도 여러분들이 쉽게 이해하실 거라 생각합니다.
참고로 생성자는 클래스가 생성될때 초기화를 담당하게 됩니다.

이상 Class에 대해서 설명을 드렸는데 감이 오시는지 모르겠네요. 사실 PJS에는 여러가지 필수 개념들이 몇개
있습니다. 이것을 이해하지 않고서는 PJS의 소스를 이해하시는데 어려움이 있을것이라 생각합니다.
각 장을 설명드리면서 이번과 같이 개념정리를 하는것이 있을텐데, 잠시 먼저 말씀드리면
bind, extend, hash, abstract 입니다. 이 개념들은 천천히 알아보도록 하고, 토요일 오후인데
좋은주말 보내세요~ ^^

posted by blankus

Ajax  |  2007/06/16 18:49
다들 저녁은 드셨는지 모르겠네요..ㅎㅎ 이 강좌를 읽는분들은 어느때인지 모르겠지만 꼭 식사는 챙겨드시기 바랍니다. ㅎㅎ

전 강좌에 이어 이번엔 PJS의 Array부분을 한번 알아보도록 하죠.

 Array하면 여러분들은 어떤생각이 떠오르십니까?? 전 iterator / Eunmerable 이 생각납니다. 같은 생각이신가요? 그럼 방향은 충분히 좋습니다.
한번 가보죠.

clear : 배열을 모두 비운다

<script>
  var foo = ['sam', 'justin', 'dan'];
  foo.clear();
  alert(foo);
</script>

실행했을때의 결과는 예상되시죠? 네 빈 배열이 되었습니다.

clone : 배열 복사

<script>
  var foo = ['sam', 'justin', 'dan'];
  var bar = foo.clone();
  alert(bar);
</script>


실행했을때의 결과는 예상되시죠? foo와 같은 배열이 복사되어집니다.
한가지 질문을 해보겠습니다.(저의 강좌를 보셨으면 예상되는 질문이겠죠?)

<script>
   var foo = ['1','2','3'];
   var bar = foo.clone();
   foo[0] = 'a';
   alert(bar);
</script>


위의 코드를 실행했을때의 결과는 어떻게 나올까요? 네 그렇습니다.  "1,2,3"이 출력됩니다. 이유를 알아볼까요? 전 강좌에서는 오브젝트의 참조변수는 포인터변수처럼 행동한다고 했습니다. 허나 위의 소스에서는 그렇게 행동하지 않는군요. 그렇습니다. clone 메소드의 내부를 잠시 살펴볼까요?

clone: function() {
    return [].concat(this);
}

예상하셨나요? 예상하시지 못했다면, 소스코드를 보시고 아시겠습니까? concat 메소드는 새로운 배열 오브젝트를 만들어 냅니다. 여기서 중요한건 "새로운"이란 단어입니다.  새롭기 때문에 clone메소드는 기존의 배열의 참조변수를 만들어 내지않고 새로운 변수를 메모리에 할당여 새로운 값을 가지게 됩니다. 그래서 전 강좌에서 배운데로 bar가 foo의 참조변수가 되지 않고 새로운 일반변수가 되어버린거죠.

compact : null / undefined의 값을 제외하고 새로운 배열 오브젝트 반환
프로그램을 짜면서 사용하게 될까? 하는 의구심을 들게하는 메소드입니다.  전 한번도 배열에 null이나 undefined가 들어간적이 없어서.ㅎㅎ (경험부족인가요?)

<script>
  var foo = ['frank', 'sue', 'sally' ,null];
  var bar = foo.compact();
  alert(bar);
  // --> frank, sue, sally
</script>


크게  설명할 부분은 없어보입니다.

each : 쉽게 for문을 연상하시면 될듯합니다.

<script>
   var foo = ['1','2','3'];
   foo.each(function(val){alert(val)});
</script>


foo 에서 각각 하나씩의 아이템을 빼서 인수로 넘기면 인수에 정의되어 있는 함수가 실행되게 됩니다. for 문과 같은 개념입니다.

first : 배열의 첫번째 아이템을 리턴한다.(단, 배열이 비어있으면 undefined를 리턴한다)

<script>
  var foo = ['a', 'b', 'c'];
  var bar = foo.first();
  // --> 'a'
</script>


flatten : 다중배열을 1차원 배열로 만들어 리턴한다.

<script>
  var foo = ['a',['a2','b2'], 'c'];
  alert(foo.flatten());
  //--> 'a', 'a2', 'b2', 'c'
</script>


from : $A 메소드와 동일한 기능을 수행한다.

<html>
<script type="text/javascript" src="prototype.js"></script>
<body>
   <input type='checkbox' id='item1' />
   <input type='checkbox' id='item2' />
   <input type='checkbox' id='item3' />
   <input type='checkbox' id='item4' />
</body>
<script>
   var foo = Array.from($('item1','item2','item3','item4'));
   //var foo = $A($('item1','item2','item3','item4'));
   foo.each(function(v){alert(v.id);});
   //--> 'item1' ,'item2' ,'item3', 'item4'가 순서대로 출력된다
</script>
</html>

indexOf : 아이템에 해당하는 아이템의 index값을 리턴한다.(단, 없는 값일경우 -1 리턴)

<script>
  var foo = ['1', '2', '3'];
  foo.indexOf(1)
  //--> 0
</script>


inspect : 배열을 String으로 반환한다. (결과의 양쪽끝에 '[', ']'를 붙여 출력한다)

<script>
   var foo = ['Apples', {good: 'yes', bad: 'no'}, 3, 34];
   alert(foo.inspect());
   //-->"['Apples', [object Object], 3, 34]"
</script>


last : 배열의 마지막 아이템 반환

<script>
  var foo = ['a', 'b', 'c'];
  var bar = foo.last();
  // --> 'c'
</script>


reduce : 하나의 아이템만을 가진 배열을 문자열로 변환하여 반환한다.(1개이상의 아이템을 가진것은 제외)

<script>
  var foo = ['1'];
  var foo2 = ['1','2'];
  var bar = foo.reduce();
  var bar2 = foo2.reduce();
   alert(typeof bar); //--> string
   alert(typeof bar2);// --> object
</script>


reverse : 배열의 순서를 뒤집는다(인자로 false일경우 일시적, 생략또는 true일때는 영구적)

<script>
  var foo = ['1', '2'];
  alert(foo.reverse(false));  //--> '2', '1'
  alert(foo); //--> '1', '2'
 
  foo.reverse();
  alert(foo); //--> '2', '1'
</script>


size : 배열의 크기를 리턴한다 (length 프로퍼티와 동일)
<script>
  var foo = ['1', '2'];
  alert(foo.size()); //--> 2
</script>

toArray : 위에서 봤던 clone과 동일한 기능을 수행

PJS에 이렇게 정의되어 있습니다. Array.prototype.toArray = Array.prototype.clone;


toJSON : json 오브젝트를 string으로 변환하여 반환 // 1.5.1에 추가된 메소드

uniq : 배열내 중복된 아이템이 있으면 제거하고 리턴


<script>
['Sam', 'Justin', 'Andrew', 'Dan', 'Sam'].uniq();
// -> ['Sam', 'Justin', 'Andrew', 'Dan']
</script>


without : 배열내에 특정 아이템을 제거하고 반환한다.

[3, 5, 6, 1, 20].without(3) 
// -> [5, 6, 1, 20]

[3, 5, 6, 1, 20].without(20, 6)
// -> [3, 5, 1]








posted by blankus
Ajax  |  2007/06/05 21:35
오늘은 바람이 좀 선선하게 불고, 날씨는 흐리고 시원했는데...왜 유독 사무실에서..... 내자리는 이리도 더울까요? 그나마 누군가가 준 미니선풍기로 더위를 살짝 달래고 있습니다. ㅎㅎ
이번엔 PJS에 Utility Method라고 명명된 부분을 해볼까 합니다. 유틸리티라고 해서 거창한건 아닙니다만, PJS에서 많이 사용되고 있는것 중에 하나이므로 알아보도록 합니다.

우선 $메소드에 대해서 알아볼까요?
$메소드는 일단 축약메소드라고 생각하시면 됩니다. 무슨이야기인지 아래의 코드를 봐주세요.

<script>
  function foo(el){
     element = $(el);
     ........
  }
</script>


아직 모르시겠죠? ㅎㅎ 위의 코드를 다시 써보면 이렇게 됩니다.

<script>
  function foo(el){
     element = document.getElementById(el);
  }
</script>

이제 아시겠죠? 그럼 단순히 이것만을 위한 메소드냐구요? 아닙니다. 또 아래를 봐주세요.

<script>
  $('item1', 'item2', 'item3').invoke('hide');
</script>


invoke 메소드가 나왔네요? 여기서는 간단하게만 설명드리겠습니다. invoke(실행할 메소드)의 식으로 사용하며 Enumerable에 속한 메소드입니다. 그러므로 위의 식을 설명드리면 $메소드에 속한 item1~3까지의 엘리먼트들이 hide메소드가 실행되어 화면에서 감춰지는 코드입니다. 어떻습니까? 위의 식을 PJS없이 그냥 코딩한다면 그만큼 코드의 길이또한 길어지게 될 것입니다. 이런면에서 보면 PJS도 어느정도의 프레임워크적인 마인드가 있는 것이라 생각되지 않습니까? ㅎㅎ 하지만 아직도 전 라이브러리라고 생각하고 있습니다. -_-;;

자... 두번째 메소드는 $A 메소드입니다. 이 메소드는 지정한 오브젝트에 대해서 배열로 반환시켜주는 역할을 합니다. 백문이 불여일견

<script>
  var foo = $A(document.getElementsByTagName('p'));
  foo.each(Element.hide);
  $(foo.last()).show();
</script>


어떻습니까? 어떤 메소드인지 느낌이 오시는지요. each메소드도 역시 Enumerable에 속해있는 메소드로 뭔가가 돌면서 반복수행하게 되는 메소드입니다. 나중에 더 자세히 설명하도록 하겠습니다.

세번째 메소드를 살펴보도록 합니다. 바로 $F 메소드인데요, 이 메소드는 아주 단순합니다.

<script>
    var foo = $F(document.getElementById('item1'));
    var bar = $F($('item1'));
</script>


foo, bar 둘다 해당 엘리먼트의 값(value)을 출력하고 있습니다. 첫번째 줄보다 두번째 둘이 더 간단네요. 이게 바로 PJS의 장점중에 하나가 아닐까 합니다.

네번째는 $H 메소드 입니다. 이름에서 H가 뭘 의미할까요? 네 Hash입니다. ㅎㅎ 역시 아래 코드를 보시죠

<script>
  var foo = $H({a:"1", b:"2"});
   alert(foo.keys());
</script>


$H 안에는 해쉬스타일의 오브젝트가 들어가 있네요. 그리고 다음줄에선 keys라는 메소드로 값을 추출하고 있구요, alert을 통해 나오는 값은 a, b가 출력됩니다.

다음 메소드는 $R 메소드 입니다. 여기서 R은 Range의 약자입니다. range는 범위를 나타내구요. 그럼 아시겠지만 범위를 나타내는 축약 메서드 입니다. 역시, 아래의 소스를 보시지요.

<script>
   var bar = $R(0,3);
   alert($A(bar).join(', '));
</script>

여기서 bar는 0 ~ 3까지 오브젝트 범위를 만들어내고, 그것을 위에서 배운 $A메소드를 통해 배열로 다시 변경하고 join을 통해 alert으로 찍히게 됩니다. 즉 값은 "0, 1, 2, 3"으로 찍히게 되는거죠. join 메소드는 따로 설명안해도 아시겠죠? ^^

축약함수의 마지막 이네요. 바로 $w 메소드 입니다(대문자 W가 아니라 소문자 w입니다).

<script>
   var foo = $w('apple banana kiwi');
   alert(foo);
</script>


보시다 시피 $w메소드에 공백(whitespace)으로 구분된 문자열이 들어가있고, 그것을 $w메소드는 공백으로 구분하여 배열로 저장하게 됩니다. 즉, 반환은 Array인거죠.

다음은 Try.these 인데요, 이것은 좀 특이합니다. try - catch에 지겨우신분들은 좋은소식이죠 ^^

getTransport: function() {
  return Try.these(
    function() { return new XMLHttpRequest() },
    function() { return new ActiveXObject('Msxml2.XMLHTTP') },
    function() { return new ActiveXObject('Microsoft.XMLHTTP') }
  ) || false;
}


소스는 위와 같습니다. 만약 getTransport가 호출되면 처음부터 하나씩 실행하면서 에러가 나면 다음 함수를 호출하고, 계속 반복해서 에러가 안나는 함수의 결과를 리턴하게 됩니다. 만약 전부 에러를 발생시킨다면 false를 반환하구요, 우리는 이것을 보통 try - catch로 하고 있는데요, PJS에는 번거롭지 않게 좋은 작업환경을 제공해주고 있습니다.

사실 이번 강좌는강좌라기 보다는 PJS를 설명하는데 꼭 필요한 것에 대해서 알아봤습니다. 좀쉽죠?
하지만 이것을 가지고 구현하는 다음장부터는 좀 어려울지도 모르겠습니다. 전에도 이야기 했는데, 익숙치 않은 방식으로 구현되고 있기때문에 어렵게 느껴지는 것이죠. 익숙해지면 좀 편하게 다가갈 것이구요.

더위를 먹었는지 잠시 글쓰는 것을 멈춰야 할 것같습니다. 좀있음 저녁먹을 시간인데
저녁을 먹고와서 다시쓰던가 해야겠네요.

posted by blankus
Ajax  |  2007/06/05 17:51

회사일이 많아 퇴근전에 글을 쓰고 있네요..ㅎㅎ 요즘 Ajax로 이런저런걸 하다보니 일이 많군요..ㅎㅎ
오늘부터는 Ajax의 프레임워크인 'PrototypeJS(이하 PJS)'를 강좌로 해볼 생각입니다. 사실 개인적인 생각으론 PJS가 프레임워크라기보단, 라이브러리에 가깝다고 생각합니다. 제가 자바개발자라서 그런지 프레임워크라고
하면 스트럿츠나,  스프링 같은 것이 먼저 떠오르기때문이죠.

위에서 나열한 자바 프레임워크들은 내부적으로 모두 MVC를 기반으로 작성되어 있으면서, 사용자로 하여금
MVC에 맞춰 개발되도록 도와줍니다. 물론 PJS도 틀을 제공하긴 하지만 MVC자체를 제공해주지는 못하고
있습니다. 나름대로 지금껏 웹개발을 해오면서 제대로된 MVC를 적용해본적은 별로 없습니다. 그나마 현재 근무하고 있는 회사에서 Ajax로 클라이언트단의 MVC를 구현하여 개발해놓게 다행이라고 해야하나? ㅎㅎ 암튼 한국 웹개발시장이 현재 그러한듯 합니다. 얼마 안되는 시간에 빠른개발을 원하다보니, 제대로 이해도 안되는
상황에서 주먹구구식으로 프레임워크에 맞춰서 개발하거나, 그도 안되는 작은 회사의 직원들은 Jsp model 1방식(jsp일때)으로 개발을 많이 하고 있습니다. "빨리빨리"의 모습이 좋은모습일 수도 있겠지만, 결코 좋은면만
가진것은 아닌것 같습니다. ㅎㅎ 이야기하다보니 또 주저리 주저리 떠들고 있군요..ㅋㅋ

오늘은 제목처럼 " PJS의 Function부분"을 진행해보자구요.
이부분은 제가 밑에 포스트한 변수범위와도 관련이 있는 부분입니다. 그 강좌를 보신 분이라면 아마도 쉽게 이해하실 수 있을거라 생각합니다.

<script>
   window.name = "the window object";

   function scopeTest(){
      alert(this.name);
   }

   scopeTest(); -------------------- ①

   var foo = {
      name : "the foo object",
      otherScopeTest : function(){
         alert(this.name);
      }
   }

   foo.otherScopeTest();------------- ②
</script>

위의 소스를 보시지요. ① 부분의 실행결과는 "the window object"가 리턴되겠죠. ② 부분의 실행결과는
"the foo object"가 되겠죠. 이해가 잘안가시는 분은 밑의 강좌를 한번 다시 읽어주세요. 계속 설명드리죠.
보시다시피 "오브젝트 안에서는 지역변수가 전역변수보다 우선한다"가 성립하는 부분입니다. 그럼 아래 코드를 봐주세요.

<script>
 var obj = {
   name : "A nice demo",
   fx : function(){
     alert(this.name);
   }
 };
 
 window.name = "I am such a beautiful window";
 
 function runFx(f){
   f();
 }

obj.fx(); ---------------------------------------- ①

 runFx(obj.fx);----------------------------------- ②

 var fx2 = obj.fx.bind(obj);
 runFx(fx2);-------------------------------------- ③
</script>


1번의 실행결과가 예상이 되나요? 그렇습니다. obj의 지역변수격인 name이 있기때문에 결과는
"A nice demo"입니다. 그러면 1번과 2번의 차이점은 무엇인가요? 네, 함수를 직접 호출해서 사용하는것과,
함수를 다른함수의 인자로넘겨 실행하는것이 다릅니다. 어떻습니까? 결과가 예상되십니까? "A nice demo"라고 답하신분이 대부분일 거라 생각합니다.. 그렇습니다.. 오답입니다 ^^; 답은 엉뚱하게도 "I am such a beautiful window"가 됩니다. 왜 이런일이 생기는 걸까요? 눈치 빠르신분은 아~! 하고 이야기 하실지도 모르겠네요.
바로 Scope의 변경때문입니다. 단순하게 obj.fx()를 실행하는 것은 오브젝트 안의 scope를 가지고 실행하기에 this.name을 찍으면 지역변수격인 "A nice demo"가  찍힙니다. 허나 두번째 방법은 좀 다릅니다.
2번의 구문을 다시 표현하면 아래와 같습니다.

function runFx(){
   alert(this.name);
}


이해가 안된다구요? 자 천천히 봅시다. runFx의 인자로 함수가 넘어갔습니다(함수를 포함한 오브젝트가
아닙니다). 그리고선 인자로 넘어온 함수는 runFx의 내부에서 실행되게 됩니다. 허나 인자로 넘어온 함수는  this.name을 찍으려고하나 runFx의 내부에는 name이란 지역변수가 존재하지 않습니다. 그래서 그 위인 전역변수중에 이름이 name 인것을 찾아 출력하려고 하고, 그결과 "I am such a beautiful window"가 출력되게 됩니다. 이해가 가셨는지요.

그럼 3번을 볼까요? 2번과는 다르게 bind라는 함수를 거쳐 runFx로 함수를 보냅니다. 결과는 ? 놀랍게도
"A nice demo"가 출력됩니다.

어째그 이런결과가 나올까요? PJS의 소스코드를 잠시 참고합니다.

Function.prototype.bind = function() {

  var __method = this, args = $A(arguments), object =  args.shift();
 
  return function() {
    return __method.apply(object,  args.concat($A(arguments)));
  }
}


PJS에는 이렇게 정의되어 있습니다.
__method에는 this가 담기는데.. 여기에선 bind 된 것이 담기므로 obj.fx가 담기게 되고,  args에는 arguments로 넘어온 obj 오브젝트가 통으로 담기게 됩니다. $A 함수는 오브젝트를 배열 오브젝트로 변경하는 역할을 담당하고 있습니다. object는 args의 shift를 담고 있는데 shift는 배열의 첫번째것을 잘라내어 담고, 나머지는
다시 자기자신에게 담는 역할을 합니다. 그러므로 처음엔 args가 arguments의 전부를 가지고 있다가 arguments의 첫번째는 object에게 주고 나머지는 다시 args에 할당됩니다. 그리고 위에서 할당된 __method를
실행하게됩니다. obj의 모든것을 넘겨받아서 내부적으로 처리하여 실행하는거죠.
소스코드보다 말이 더 어렵네요 ^^; 하핫

Function안에 있는 bindAsEventListener 또한 같은 원리입니다. 다만 Event라는 것만 다르지 다른것은
똑같습니다. 원본소스는 아래와 같습니다.

Function.prototype.bindAsEventListener = function(object) {
  var __method = this, args = $A(arguments), object = args.shift();
  return function(event) {
    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  }
}


위의 소스설명은 하지 않겠습니다. 제가 설명한 부분을 참고하시어 직접 분석해보세요.

이상으로 PJS에 있는 Function부분을 마치겠습니다.

posted by blankus

Ajax  |  2007/06/01 22:01