jQuery原始碼分析之=>jQuery的定義

橙子瓣發表於2015-02-03

最近寫前段的程式碼比較多,jQuery是用的最多的一個物件,但是之前幾次看了原始碼,都沒搞清楚jQuery是怎麼定義的,今天終於看明白怎麼回事了。記錄下來,算是一個新的開始吧。

(文中原始碼都是jQuery-1.10.2版本的)

 

先上一段jQuery定義的原始碼,定義了jQuery為一個function

1     // Define a local copy of jQuery
2     jQuery = function( selector, context ) {
3         // The jQuery object is actually just the init constructor       'enhanced'
4         return new jQuery.fn.init( selector, context, rootjQuery );
5     } 

 

這就是我們常用的格式:$("#div1");就是通過這個函式定義的

這個函式只有一行有效程式碼,就是例項化了一個型別,這個型別是在jQuery的原型中定義的,那麼繼續往下看。

 

  1 jQuery.fn = jQuery.prototype = {
  2     // The current version of jQuery being used
  3     jquery: core_version,
  4 
  5     constructor: jQuery,
  6     init: function( selector, context, rootjQuery ) {
  7         var match, elem;
  8 
  9         // HANDLE: $(""), $(null), $(undefined), $(false)
 10         if ( !selector ) {
 11             return this;
 12         }
 13 
 14         // Handle HTML strings
 15         if ( typeof selector === "string" ) {
 16             if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
 17                 // Assume that strings that start and end with <> are HTML and skip the regex check
 18                 match = [ null, selector, null ];
 19 
 20             } else {
 21                 match = rquickExpr.exec( selector );
 22             }
 23 
 24             // Match html or make sure no context is specified for #id
 25             if ( match && (match[1] || !context) ) {
 26 
 27                 // HANDLE: $(html) -> $(array)
 28                 if ( match[1] ) {
 29                     context = context instanceof jQuery ? context[0] : context;
 30 
 31                     // scripts is true for back-compat
 32                     jQuery.merge( this, jQuery.parseHTML(
 33                         match[1],
 34                         context && context.nodeType ? context.ownerDocument || context : document,
 35                         true
 36                     ) );
 37 
 38                     // HANDLE: $(html, props)
 39                     if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
 40                         for ( match in context ) {
 41                             // Properties of context are called as methods if possible
 42                             if ( jQuery.isFunction( this[ match ] ) ) {
 43                                 this[ match ]( context[ match ] );
 44 
 45                             // ...and otherwise set as attributes
 46                             } else {
 47                                 this.attr( match, context[ match ] );
 48                             }
 49                         }
 50                     }
 51 
 52                     return this;
 53 
 54                 // HANDLE: $(#id)
 55                 } else {
 56                     elem = document.getElementById( match[2] );
 57 
 58                     // Check parentNode to catch when Blackberry 4.6 returns
 59                     // nodes that are no longer in the document #6963
 60                     if ( elem && elem.parentNode ) {
 61                         // Handle the case where IE and Opera return items
 62                         // by name instead of ID
 63                         if ( elem.id !== match[2] ) {
 64                             return rootjQuery.find( selector );
 65                         }
 66 
 67                         // Otherwise, we inject the element directly into the jQuery object
 68                         this.length = 1;
 69                         this[0] = elem;
 70                     }
 71 
 72                     this.context = document;
 73                     this.selector = selector;
 74                     return this;
 75                 }
 76 
 77             // HANDLE: $(expr, $(...))
 78             } else if ( !context || context.jquery ) {
 79                 return ( context || rootjQuery ).find( selector );
 80 
 81             // HANDLE: $(expr, context)
 82             // (which is just equivalent to: $(context).find(expr)
 83             } else {
 84                 return this.constructor( context ).find( selector );
 85             }
 86 
 87         // HANDLE: $(DOMElement)
 88         } else if ( selector.nodeType ) {
 89             this.context = this[0] = selector;
 90             this.length = 1;
 91             return this;
 92 
 93         // HANDLE: $(function)
 94         // Shortcut for document ready
 95         } else if ( jQuery.isFunction( selector ) ) {
 96             return rootjQuery.ready( selector );
 97         }
 98 
 99         if ( selector.selector !== undefined ) {
100             this.selector = selector.selector;
101             this.context = selector.context;
102         }
103 
104         return jQuery.makeArray( selector, this );
105     },
106         。。。其他方法省略
107 }

在上面這段程式碼中,定義了jQuery的原型,然後將原型賦給了jQuery.fn;$.fn這個東西大家應該都很熟悉了,我們擴充套件jQuery外掛的時候時長用到它,其實就是想jQuery的原型中新增了方法。

在原型的定義中重要的就是init函式了,有三個引數:selector,context,rootjQuery。

在這個函式中根據各種情況做了判斷,比如:selector物件為false時的就是處理$("")或者$(null)或者$(undefined),(註釋上都有說明);

如果selector是function時,就是我們最常用的頁面載入處理函式:$(function(){......});

到這裡,應該就可以明白了,jQuery物件不過是一個函式,然後在內部例項化了一個jQuery.fn.init;

但是問題來了,我們做jQuery外掛的時候,擴充套件的是jQuery.fn,是jQuery的原型,跟jQuery.fn.init的例項化物件沒有關係,這是怎麼回事?

接下來這行程式碼就解決了我的疑惑:

1 // Give the init function the jQuery prototype for later instantiation
2 jQuery.fn.init.prototype = jQuery.fn;

將jQuery原型的定義賦給了jQuery.fn.init的原型,這樣我們在擴充套件jQuery原型的同時,也擴充套件了jQuery.fn.init的原型,那麼jQuery物件就有了那些方法(確切的說應該是jQuery.fn.init型別的例項化物件)

接下來的原始碼定義了jQuery.extend=jQuery.fn.extend=function(){};然後通過這兩個方法新增了預定義的方法和屬性,比如:ready函式、isFunction函式、each函式等等

到這裡差不多jQuery的定義就結束了,我們常用的一些方法也在原始碼中通過extend方法預先定義好了。

 

相關文章