이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌31 - JavaScript - 2년 전 등록 / 7달 전 수정

jQuery 분석(1)

조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요! 이번 시간은 고급 강좌의 첫 번째로, jQuery 분석을 하겠습니다. 말이 고급 강좌지 사실 그냥 사소한 정보입니다. 응용 시간이라고 보시면 될 거에요.

jQuery는 정말 많이 쓰는 라이브러리인데요. 웹 개발자뿐만 아니라 디자이너도 사용하는 대중적인 라이브러리입니다. 아마 자바스크립트 라이브러리 중에서 제일 인기가 많을 거에요. 지금은 현재 3버전까지 나와서 꾸준히 발전하고 있는데요. jQuery는 최신 버전을 쓰는 게 좋습니다. 최신 버전이 최적화가 잘 되어있기 때문이죠.

jQuery에 대한 강좌를 여기서 하는 것은 아니고요. 지금 따로 할까 생각중에 있습니다만, jQuery가 워낙 간단해서 할 필요성을 잘 못느끼고 있습니다. 이 강좌에서는 jQuery가 어떻게 구성되어 있는지 코드를 분석할 겁니다.

jQuery 1.0.1 버전을 사용할건데요. 최초의 버전(1.0.0은 못 구하겠네요)이자 핵심 기능만 담고 있기 때문에 분석하기 편해서 선택했습니다. jQuery 코드 전체를 직접 보고 싶다면 code.jquery.com/jquery-버전.js 하면 됩니다. 어떤 것을 분석할 지는 다음과 같습니다.

  • jQuery는 어떻게 $를 사용하는가
  • $(function() { 내용 });은 어떻게 기능하는가 
  • $(선택자)는 어떻게 기능하는가
  • 제이쿼리 플러그인에서 사용하는 $.fn은 무슨 원리인가 
  • $(선택자).find().css()... 등 메소드 체이닝은 어떻게 기능하는가

후, 알아볼게 많네요. 일단 jQuery와 $부터 알아보죠.

function jQuery(a,c) {
  // Shortcut for document ready (because $(document).each() is silly)
  if ( a && a.constructor == Function && jQuery.fn.ready )
    return jQuery(document).ready(a); // (1)
 // Make sure that a selection was provided
  a = a || jQuery.context || document; // (2)
 // Watch for when a jQuery object is passed as the selector
  if ( a.jquery )
    return $( jQuery.merge( a, [] ) ); // (3)
 // Watch for when a jQuery object is passed at the context
  if ( c && c.jquery )
    return $( c ).find(a); // (4)
 // If the context is global, return a new object
  if ( window == this )
    return new jQuery(a,c); // (5)
 // Handle HTML strings
  var m = /^[^<]*(<.+>)[^>]*$/.exec(a);
  if ( m ) a = jQuery.clean( [ m[1] ] ); // (6)
 // Watch for when an array is passed in
  this.get( a.constructor == Array || a.length && !a.nodeType && a[0] != undefined && a[0].nodeType ?
  // Assume that it is an array of DOM Elements
  jQuery.merge( a, [] ) :
 // Find the matching elements and save them for later
  jQuery.find( a, c ) ); // (7)
 // See if an extra function was provided
  var fn = arguments[ arguments.length - 1 ]; // (8)
 // If so, execute it in context
  if ( fn && fn.constructor == Function ) // (9)
    this.each(fn);
}
// Map over the $ in case of overwrite
if ( typeof $ != "undefined" )
  jQuery._$ = $;
// Map the jQuery namespace to the '$' one
var $ = jQuery;

jQuery라는 함수를 엄청 복잡하게 정의해 놓았네요. 코드가 길어서 위에서부터 순서대로 (1), (2), (3)... 매겨놓았습니다. 위의 코드 마지막의 var $ = jQuery가 $를 jQuery와 동일시하는 코드입니다. 그래서 jQuery를 써도 되고, $를 써도 됩니다.

그렇다면 $(function() { 내용 }) 을 했을 때 어떻게 동작할까요? $() 안에 함수를 넣은 것이기 때문에 a가 함수가 됩니다. 다행히 (1)에서 바로 걸리네요. a가 함수이므로 jQuery(document).ready(a); 가 실행됩니다. $(document).ready(function() {})$(function() {}) 과 같은 거였군요.

이제 $(document)처럼 $(선택자) 했을 경우 무슨 상황이 일어나나 알아볼까요? jQuery의 매개변수 a에 문자열 선택자 이름이 들어갑니다. (1)은 함수가 아니니 건너뛰고요, (2)는 그대로 a로 갑니다. a는 문자열이지 jQuery 객체가 아니므로 (3)도 넘어갑니다. (4)는 c가 없으니까 넘어가고요,

드디어 (5)에서 걸립니다! 근데 new jQuery(a, c)라서 결국 똑같은 과정을 반복하게 됩니다. new를 하는 이유는 prototype을 사용하기 위함인 것 같습니다. 그런데 이번에 new jQuery로 호출했을 때는 (5)에서 걸리지 않습니다. new로 호출했을 때는 this가 window가 아니니까요. (6)은 문자열이 태그를 포함한 게 아니라 넘어가고요. 

(7)에서 마지막으로 걸립니다! array도 아니고 Node도 아니기 때문에 jQuery.find가 작동합니다. 그런데 jQuery.find는 위 코드에 없네요? 이제 jQuery의 prototype을 정의해둔 코드를 봐야겠습니다...

jQuery.fn = jQuery.prototype = {
  jquery: "$Rev: 509 $",
  ...,
  get: function( num ) {
    // Watch for when an array (of elements) is passed in
    if ( num && num.constructor == Array ) {
      // Use a tricky hack to make the jQuery object
      // look and feel like an array
      this.length = 0;
      [].push.apply( this, num );
      return this;
    } else
    return num == undefined ?
      // Return a 'clean' array
      jQuery.map( this, function(a){ return a } ) :
      // Return just the object
      this[num];
  },
  ...,
  find: function(t) {
    return this.pushStack( jQuery.map( this, function(a){
      return jQuery.find(t,a);
    }), arguments );
  },
  ...,
  pushStack: function(a,args) {
    var fn = args && args[args.length-1];
    if ( !fn || fn.constructor != Function ) {
      if ( !this.stack ) this.stack = [];
      this.stack.push( this.get() );
      this.get( a );
    } else {
      var old = this.get();
      this.get( a );
      if ( fn.constructor == Function )
        return this.each( fn );
      this.get( old );
    }
    return this;
  }
}

코드 분석에 앞서 이 코드의 제일 위를 보시면 jQuery.fn = jQuery.prototype이라고 되어있습니다. 제이쿼리 플러그인을 만들 때 사용하던 fn이 prototype이었던거죠. prototype에 추가했기 때문에 확장함수를 모든 제이쿼리 객체가 쓸 수 있던 겁니다. 다시 find로 돌아가서 find를 보면 안에 다시 jQuery.find를 호출하고 있습니다. 이해가 잘 안 가네요. 태그를 찾는 코드 대신에 find를 다시 호출하고 있는데요. 또 map 메소드는 보이지도 않습니다. 어떻게 된 걸까요? 그 비밀은 다음 강좌에서 알려드리겠습니다!

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

1개의 댓글이 있습니다.
7달 전
마지막 코드부분에 상단부분은 코드창이 아니라 텍스트로 들어가야 할 것 같아요ㅠ
보기가 너무 힘드네요
7달 전
아 그건 텍스트랑 코드가 섞였네요. 수정했습니다.