프로그램언어/javascript 프레임웍

jquery vs prototype

멋진꿈 2009. 1. 5. 08:56

먼저 최근에 경향을 보자면은 jquery vs prototype 의 경쟁? 구도입니다.

예전에는 prototype이 대세였는데

몇개월전부터 jquery가 주목받고있는 형태입니다.

어떤것을 적용하는것이 좋을지는 각자개발자가 선택할 목이지면 꼼꼼히 생각해봐야할부분이라고생각합니다.


jQuery
는 겸손한(unobtrusive) 자바스크립트를 위한 자바스크립트 라이브러리다. jQuery는 태생적으로 BDD(Behavior driven development) 방법론을 지향하고 CSS 셀렉터를 사용하여 HTML 문서를 훓는 방법(traversing)에 기반한다. 반면에 Prototype은 태생적으로 CDD(Class driven development)를 지향하고 쉽게 자바스크립트 개발할 수 있도록 해준다. Prototype 라이브러리Ruby on Rails에서 아주 잘 지원되고 많은 헬퍼 함수들을 가지고 있다.

- 내가 Prototype에서 jQuery로 옮긴 이유 중에서...
jQuery.js에서 $()를 호출할때마다 거대한 jQuery객체를 만들어내지는 않는다. jQuery 객체들은 prototype-based-inheritance를 통해서 jQuery를 상속받은 클론들이다. 이런 클론들은 delegation pointer를 가지고 원본(prototype)의 property와 method들을 참조하기 때문에 무겁지 않다.

- Prototype vs jQuery 중에서...

Prototype기반으로 만들어진 프로젝트를 jQuery로 옮기고 있습니다. jQuery로 만들어진 것을 Prototype용으로 컨버전(conversion) 하는 작업은 간간히 해 왔지만 이런 경우는 처음이네요. 위와 같은 이유들도 있겠지만 조금 더 보태자면 야속하게도 약 9개월 동안 이렇다 할 업데이트가 이루어지지 않는 것과 지금은 jQuery가 대세이기 때문이기도 합니다. 요즘 배포되는 자바스크립트 플러그인들을 보면 대부분이 jQuery기반이라 것을 눈여겨 보지 않아도 알 수 있을 정도입니다. Prototype에서 jQuery로 컨버전하는 동안 주요한 변경내역을 기록해 보았습니다. 참고로 처음에 위치한 코드가 Prototype이고 아래쪽에 위치한 것이 jQuery의 코드입니다.

DOM selector

<PRE class=parsed>$('element'); //=> DOM element object

$(
'#element'); //=> jQuery(Array-like Object) object


$('element'); //=> DOM element object

$('#element').get(0); //=> DOM element object


$('element1','element2','element3'); //=> Array

$('#element1,#element2,#element3'); //=> jQuery object


element.down('div.item'); //=> DOM element object

element.children('div.item'); //=> jQuery object


element.up('div.contents'); //=> DOM element object

element.parent('div.contents'); //=> jQuery object


element.up('div.container'); //=> DOM element object

element.parents('div.container:first'); //=> jQuery object


element.next('div.item'); //=> DOM element object

element.next('div.item'); //=> jQuery object


element.previous('div.item'); //=> DOM element object

element.prev('div.item'); //=> jQuery object


element.select('div.item'); //=> Array

element.find('div.item'); //=> jQuery object


$('element').select('input[type="button"]'); //=> Array

$('#element input[type=button]'); //=> jQuery object


$$('#element a[href=#]')[0]; //=> DOM element object

$('#element a[href=#]')[0]; //=> DOM element object


element.select('input.cancel', 'input.submit', 'a.close'); //=> Array

element.find('input.cance,linput.submit,a.close'); //=> jQuery object


element.nextSiblings('div.item'); //=> Array

element.nextAll('div.item'); //=> jQuery object


element.previousSiblings('div.item'); //=> Array

element.prevAll('div.item'); //=> jQuery object

</PRE>

초반에 가장 이해하기 힘든것이
DOM 선택자였는데, '$'가 마치 오퍼레이터(operator) 처럼 사용되는 것은 비슷합니다. Prototype의 '$'는 보통 document.getElementById의 대역으로 사용되며, Element.prototype의 메서드들을 상속시키는 역활인 반면 jQuery의 '$'는 CSS 선택자와 동일한 룰을 가지고 있으며, Prototype의 select 메서드를 혼용하거나 $$유틸리티를 사용하는 효과가 발생합니다. 단순히 선택 방법만 틀린것이 아니라 처리방법 역시 다릅니다. jQuery의 '$'선택자에 의해 선택된 DOM 요소들은
배열화된 객체(Array-like Object)로 관리되는데 이것을 보통 'jQuery 객체'라고 부르더군요. jQuery에서 제공하는 여러 체인 메서드들은 jQuery 객체화한 다음에야 비로소 혼용될 수 있습니다. 만약, Prototype과 마찬가지로 DOM 요소를 반환하게 하려면 'jQuery 객체에 get(0)' 또는 '[0]'을 사용해야 합니다. 이 것은 더이상 체인 메서드를 사용할 수 없는 상태가 되기 때문에 '$'를 다시 호출하는 등 사용성 및 가독성을 떨어트릴 수 있으므로 가급적이면 변수에 할당하여 사용하거나 체인 메서드로 모두 처리할 것을 권장하고 있습니다.

Prototype의 'down'과 'up'에 해당하는 'children'과 'parent'메서드는 서로 차이를 보입니다. Prototype의 경우 자식/부모 노드들을 순차적으로 검색하여 발견되는 첫번째 노드를 반환하는 반면, jQuery는 첫번째 자식/부모 노드중에서 일치하는 것만을 찾아 반환합니다. 때문에 이를 대체하기 위해서는 때때로 'find'와 'parents'메서드를 혼용해야 할 필요가 있습니다.

Event

<PRE class=parsed>var expand = function(event) {
  event.
element(); //=> DOM element object
  event.target;
//=> DOM element object


  event.pointerX(); //=> absolute horizontal position for a mouse event 

  event.clientX; //=> absolute vertical position for a mouse event


  event.pointerY(); //=> absolute horizontal position for a mouse event 

  event.clientY; //=> absolute vertical position for a mouse event


  event.stop(); // stop bubbling 

  event.stopPropagation(); // stop bubbling
};


element.observe('mouseup', expand); //=> DOM element object

element.bind('mouseup', expand); //=> jQuery object


document.observe('dom:loaded', expand); //=> document object

$(document).ready(expand); //=> jQuery object


element.stopObserving('mouseup', expand); //=> DOM element object

element.unbind('mouseup', expand); //=> jQuery object


element.observe('click', expand); //=> DOM element object

element.click(expand); //=> jQuery object

</PRE>
Prototype에 익숙한 사용자라면 bind가 눈에 띨 것입니다. jQuery에서 bind는 Prototype의 observe와 동일한 메서드로 볼 수 있습니다. 이 밖에도 jQuery는 편이성을 위해 jQuery 객체에 체인되는 이벤트 메서드들(keypress, mouseover, dblclick 등)을 제공하고 있습니다.

Element

<PRE class=parsed style="PADDING-BOTTOM: 25px">element.remove(); //=> removed DOM element object

element.
remove(); //=> removed jQuery object


var img = new Element('img',{src:'/images/sample.gif',id:'my-img',alt:'img'}); //=> DOM element object

var img = $('<img src="/images/sample.gif" id="my-img" alt="img">'); //=> jQuery object


var el= new Element('a',{href:'http://firejune.com',className:'link'}).update('ClickMe!'); //=> DOM element object

var el = $('<a href="http://firejune.com" class="link">').html('ClickMe!'); //=> jQuery object


element.update('<p>hello!</p>'); //=> DOM element object

element.html('<p>hello!</p>'); //=> jQuery object


element.insert({top:img}); //=> DOM element object

element.prepend(img); //=> jQuery object


element.insert({bottom:'<p>hello!</p>'}); //=> DOM element object

element.append('<p>hello!</p>'); //=> jQuery object


element.insert({before:el}); //=> DOM element object

element.before(el); //=> jQuery object


element.insert({after:'<p>hello!</p>'}); //=> DOM element object

element.after('<p>hello!</p>'); //=> jQuery object

</PRE>
DOM 요소를 파싱할 때 주로 다루게 되는 메서드들입니다. jQuery에서는 DOM 요소의 선택자로 사용되는 '$'를 그대로 재사용하여 엘리먼트를 생성하는 모습은 매우 인상적입니다.

Style

<PRE class=parsed>element.setStyle({width: '100px'}); //=> DOM element object

element.
css({width: 100}); //=> jQuery object


element.getStyle('width'); //=> '100px'

element.css('width'); //=> '100px'


element.getWidth(); //=> 100

element.width(); //=> 100


element.getHeight(); //=> 100

element.height(); //=> 100


element.hide(); //=> DOM element object

element.hide(); //=> jQuery object


element.show(); //=> DOM element object

element.show(); //=> jQuery object


element.addClassName('active'); //=> DOM element object

element.addClass('active'); //=> jQuery object


element.removeClassName('active'); //=> DOM element object

element.removeClass('active'); //=> jQuery object


element.hasClassName('active'); //=> DOM element object

element.hasClass('active'); //=> jQuery object


element.toggleClassName('active'); //=> DOM element object

element.toggleClass('active'); //=> jQuery object

</PRE>엘리먼트 스타일과 관련해서는 서로 비슷한 양상을 보이지만, jQuery의 경우 'css'메서드를 통해서 스타일을 부여하는 것과 값을 읽어내는 것이 동시에 이루어지고 있다는 점에서 차이가 있습니다.


Enumerable

<PRE class=parsed>elements.each((function(element, index) {
  
//=> DOM element object, index
});

elements.
each(function(index, element) {
  
//=> DOM element object, index
});


elements.invoke('remove');
elements.remove();
</PRE>배열 객체를 다루는데에는 두말할 나위 없이 jQeury가 10점 만점에 10점입니다. 단, Prototype과 달리 jQuery에서 each를 사용할 때 주의할 점은 index와 반환요소의 순서가 뒤바뀐 것 입니다.

Ajax

<PRE class=parsed>var form = $(formElement);


new Ajax.Request(form.action, {
  method: form.method, paramitors: form.serialize(),
  onSuccess: function(transport) {
    //=> transport object(Text or JSON or XML)
  },
  onFailure: function(transport) {},
  onComplete: function(transport) {}
});


$.ajax({
  url: form.get(0).action, type: form.get(0).method, data: form.serialize(),
  success: function(transport) {
    //=> transport string or json object or xml node
  },
  error: function(transport) {},
  complete: function(transport) {}
});
</PRE>응답해더의 'content-type'에 따라 결과물을 자동으로 분석하여 반환하는 Ajax로직은 서로 닮았습니다만, jQuery의 경우는 응답결과에 따라 자료형이 변경되어 넘어오는 반면, Prototype은 transport객체에 모든 상황이 반영되어 넘어옵니다.

Effect

<PRE class=parsed>new Effect.Fade(element, {duration: 0.5}); // => instance of effect
element.
fadeOut(500); //=> jQuery object


new Effect.Appear(element, {duration: 0.5}); // => instance of effect
element.fadeIn(500); //=> jQuery object


new Effect.SlideUp(element, {duration:0.5,afterFinish:callback}); // => instance of effect
element.slideUp(500, callback); //=> jQuery object


new Effect.SlideDown(element, {duration:0.5,afterFinish:callback}); // => instance of effect
element.slideDown(500, callback); //=> jQuery object



new Effect.Morph(element, {
  style: 'width: 100px; height: 100px; opacity: 0.5;',
  duration: 0.5,
  transition: Effect.Transitions.linear,
  afterFinish: callback
}); // => instance of effect


element.animate(
  {width: 100, height: 100, opacity: 0.5}, // style properties
  500, // duration
  'linear', // easing
  callback
); //=> jQuery object
</PRE>jQuery는 Scriptaculous의 effect.js를 포함해야 지만 가능한 몇몇 애니메이션 효과를 추가작업 없이 사용할 수 있습니다. 애니메이션 코어를 내장하고 있기 때문입니다. 1차원적인 계산에서 그치는 Scriptaculous의 transition과는 달리 jQuery의 easing은 p, n, firstNum, diff 총 4개의 인자로 구성되는 계산식은 매우 독창적인 변화를 만들어 낼 수 있는 구조를 가지고 있습니다. 그리고 jQuery에는 Scriptaculous를 대신하는 ui.js가 있습니다. 다수의 UI 컴포넌트와 효과들로 구성되어 있으며, 메서드 단위로 파일이 나뉘어 있습니다.

Browser detection

<PRE class=parsed>Prototype.Browser.IE; //=> true or false
$.browser.msie;
//=> true or false



Prototype.Browser.Webkit; //=> true or false
$.browser.safari; //=> true or false



Prototype.Browser.Gecko; //=> true or false
$.browser.mozilla; //=> true or false


Prototype.Browser.Opera; //=> true or false

$.browser.opera; //=> true or false
</PRE>jQuery쪽이 더욱 간결해 보이네요.

jQuery를 사용할 때 가장 만족스러운 점은 싱글 혹은 멀티플 노드의 개념을 떨쳐 버림으로서 찾아오는 강력한 체인 메서드의 연결입니다. 익숙해 지기만하면 상당히 빠른 속도로 코딩을 작렬(?)할 수 있을 것으로 기대되지만, 역시 Prototype의 Class와 bind(Function.prototype)를 혼용한 클래스 지향 코딩을 구사할 수 없다는 것이 무척이나 아쉬운 점으로 남습니다. 그래서, Prototype의 쓸만한 코드들을 훔쳐 쓰려고 마음먹고 있죠.