深入分析jQuery.prototype.init選擇器源碼

轉自:http://www.cnblogs.com/yangjunhua/archive/2013/01/09/2853558.html


一、源碼思路分析總結

[ 作者:華子yjh ]

概要:

jQuery的核心思想可以簡單概括爲“查詢和操作dom”,今天主要是分析一下jQuery.prototype.init選擇器構造函數,處理選擇器函數中的參數;

這個函數的參數就是jQuery()===$()執行函數中的參數,可以先看我之前寫的淺析jQuery基礎框架一文,瞭解基礎框架後,再看此文。


思路分析:

以下是幾種jQuery的使用情況(用於查詢dom),每種情況都返回一個選擇器實例(習慣稱jQuery對象(一個nodeList對象),該對象包含查詢的dom節點):

1、處理 $(""), $(null), $(undefined), $(false)

如果參數爲以上非法值,jQuery對象不包含dom節點


2、處理 $(DOMElement)

如果參數爲節點元素,jQuery對象包含該參數節點元素,並分別增加屬性值爲參數節點元素、1的context、length屬性和用[]訪問jQuery對象中dom節點的用法

例2.1:

1 var obj = document.getElementById('container'),
2 jq = $(obj);
3 
4 console.log(jq.length); //1
5 console.log(jq.context); //obj
6 console.log(jq.[0]); //obj


3、處理$(HTML字符串)

如果第一個參數爲HTML字符串,jQuery對象包含由jQuery.clean函數創建的fragment文檔碎片中的childnodes節點

例3.1:

1 var jqHTML = $('<h1>文章標題</h1><p>內容</p>');
2 console.log(jqHTML); //[<h1>,<p>];

如果第一個參數(HTML字符串)爲一個空的單標籤,且第二個參數context爲一個非空純對象

例3.2:

1 var jqHTML = $('<div></div>', { class: 'css-class', data-name: 'data-val' });
2 
3 console.log(jqHTML.attr['class']); //css-class
4 console.log(jqHTML.attr['data-name']); //data-val


4、處理$(#id)

如果第一個參數是一個#加元素id,jQuery對象包含唯一擁有該id的元素節點,
並分別增加屬性值爲document、參數字符串、1、的context、selector、length屬性和用[]訪問jQuery對象中dom節點的用法

例4.1:

1 var jq = $('#container');
2 
3 console.log(jq.[0]); //包含的dom節點元素
4 console.log(jq.length); //1
5 console.log(jq.context); //document
6 console.log(jq.selector); //container


5、處理$(.className)

如果第一個參數是一個.className,jQuery對象中擁有class名爲className的標籤元素,並增加一個屬性值爲參數字符串、document的selector、context屬性

實際執行代碼爲:

1 return jQuery(document).find(className);


6、處理$(.className, context)

如果第一個參數是.className,第二個參數是一個上下文對象(可以是.className(等同於處理$(.className .className)),jQuery對象或dom節點),
jQuery對象包含第二個參數上下文對象中擁有class名爲className的後代節點元素,並增加一個context和selector屬性

實際執行代碼爲:

1 return jQuery(context).find(className);

例6.1:

html代碼:

1 <div class="main">
2 <h2 class="title">主內容標題</h2>
3 <p>主標題</p>
4 </div>
5 
6 <div class="sub">
7 <h2 class="title">次內容標題</h2>
8 <p>次標題</p>
9 </div>

JavaScript代碼:

 1 var jq, context;
 2 
 3 context = '.sub';
 4 var jq = $('.title', context);
 5 console.log(jq.text()); //次內容標題
 6 console.log(jq.context); //document
 7 console.log(jq.selector); //.sub .title
 8 
 9 context = $('.sub');
10 var jq = $('.title', context);
11 console.log(jq.text()); //次內容標題
12 console.log(jq.context); //document
13 console.log(jq.selector); //.sub .title
14 
15 context = $('.sub')[0];
16 var jq = $('.title', context);
17 console.log(jq.text()); //次內容標題
18 console.log(jq.context); //className爲sub的節點元素
19 console.log(jq.selector); //.title

7、處理$(fn)

如果第一個參數是fn函數,則調用$(document).ready(fn);

例7.1:

1 $(function(e){
2     console.log('DOMContent is loaded');
3 })
4 //上面代碼等同於:
5 jQuery(document).ready(function(e) {
6     console.log('DOMContent is loaded');
7 });

 

8、處理$(jQuery對象)

如果第一個參數是jQuery對象,上面已經分析過如果在查詢dom時,參數是一個#加元素id,返回的jQuery對象會增加一個屬性值爲參數字符串、document的selector、context屬性

例8.1:

1 var jq = $('#container');
2 console.log(jq.selector); // #container
3 console.log(jq.context); // document

那麼當出現$($('#container'))該如何處理呢?同樣的,返回的jQuery對象同情況5和6處理的情況一樣

例8.2:

1 var jq2 = $($('#container'));
2 console.log(jq2.selector); // #container
3 console.log(jq2.context); // document

 

二、源碼註釋分析

[ 基於jQuery1.8.3 ]


  1 var rootjQuery = $(document),
  2     rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;    
  3 
  4 jQuery.fn = jQuery.prototype = {
  5     init: function( selector, context, rootjQuery ) {
  6         var match, elem, ret, doc;
  7 
  8         // Handle $(""), $(null), $(undefined), $(false)
  9         if ( !selector ) {
 10             return this;
 11         }
 12 
 13         // Handle $(DOMElement)
 14         if ( selector.nodeType ) {
 15             this.context = this[0] = selector;
 16             this.length = 1;
 17             return this;
 18         }
 19 
 20         // Handle HTML strings
 21         if ( typeof selector === "string" ) {
 22             if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
 23                 // Assume that strings that start and end with <> are HTML and skip the regex check
 24                 match = [ null, selector, null ];
 25 
 26             } else {
 27                 match = rquickExpr.exec( selector );
 28             }
 29 
 30             // Match html or make sure no context is specified for #id
 31             // match[1]不爲null,則爲html字符串,match[2]不爲null,則爲元素id
 32             if ( match && (match[1] || !context) ) {
 33 
 34                 // HANDLE: $(html) -> $(array)
 35                 if ( match[1] ) {
 36                     context = context instanceof jQuery ? context[0] : context;
 37                     doc = ( context && context.nodeType ? context.ownerDocument || context : document );
 38 
 39                     // scripts is true for back-compat
 40                     // selector是由文檔碎片中的childnodes組成的數組
 41                     selector = jQuery.parseHTML( match[1], doc, true );
 42                     
 43                     // 如果match[1]爲空的單標籤元素(如:<div><div>)且context爲對象字面量
 44                     if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
 45                         
 46                         // 如果context對象不爲空,則將對象中的屬性添加到selector數組中僅有的dom節點中
 47                         this.attr.call( selector, context, true );
 48                     }
 49                     
 50                     // merge函數的參數應該爲兩個數組,目的是將第二個數組中的項合併到第一個數組,而this並不是一個數組,
 51                     // this是選擇器init構造函數的實例對象,該對象繼承jQuery.prototype對象中的length屬性(默認爲0),因此可以理解好merge函數源碼
 52                     // 將selector中的dom項合併到this對象中,並返回該對象
 53                     return jQuery.merge( this, selector );
 54 
 55                 // HANDLE: $(#id)
 56                 } else {
 57                     elem = document.getElementById( match[2] );
 58 
 59                     // Check parentNode to catch when Blackberry 4.6 returns
 60                     // nodes that are no longer in the document #6963
 61                     if ( elem && elem.parentNode ) {
 62                         // Handle the case where IE and Opera return items
 63                         // by name instead of ID
 64                         // ie6,7和Opera存在此bug,當一個標籤name和一個標籤id值相等時,
 65                         // document.getElementById(#id)函數將返回提前出現的標籤元素
 66                         if ( elem.id !== match[2] ) {
 67                             // 如果存在以上Bug,則返回由find函數返回的document文檔的後代元素集合
 68                             return rootjQuery.find( selector );
 69                         }
 70 
 71                         // Otherwise, we inject the element directly into the jQuery object
 72                         this.length = 1;
 73                         this[0] = elem;
 74                     }
 75 
 76                     this.context = document;
 77                     this.selector = selector;
 78                     return this;
 79                 }
 80 
 81             // HANDLE: $(expr, $(...))
 82             // context不存在或者context爲jQuery對象
 83             } else if ( !context || context.jquery ) {
 84                 return ( context || rootjQuery ).find( selector );
 85 
 86             // HANDLE: $(expr, context)
 87             // (which is just equivalent to: $(context).find(expr)
 88             // context爲className或者dom節點元素
 89             } else {
 90                  // 等同於jQuery(context).find(selector)
 91                 return this.constructor( context ).find( selector );
 92             }
 93 
 94         // 處理$(fn)===$(document).ready(fn)
 95         } else if ( jQuery.isFunction( selector ) ) {
 96             return rootjQuery.ready( selector );
 97         }
 98         
 99         // 處理$(jQuery對象)
100         if ( selector.selector !== undefined ) {
101             this.selector = selector.selector;
102             this.context = selector.context;
103         }
104 
105         // 當第一個參數selector爲jQuery對象時,將selector中的dom節點合併到this對象中,並返回this對象
106         return jQuery.makeArray( selector, this );
107     }
108 }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章