聲明:本文爲原創文章,如需轉載,請註明來源並保留原文連接Aaron,謝謝!javascript
打開jQuery源碼,一眼看去處處都充斥着正則表達式,jQuery框架的基礎就是查詢了,查詢文檔元素對象,因此狹隘的說呢,jQuery就是一個選擇器,並這個基礎上構建和運行查詢過濾器!css
工欲善其事,必先利其器,因此先從正則入手
咱們來分解一個表達式html
// A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
做者的解釋呢很簡單,一個簡單的檢測HTML字符串的表達式html5
分解:java
1. 經過選擇|分割二義,匹配^開頭或者$結尾 node
2. ^(?:\s*(<[\w\W]+>)[^>]* 正則表達式
3. #([\w-]*))$數組
4. 還要穿插一下exec方法瀏覽器
因此綜合起來呢大概的意思就是:匹配HTML標記和ID表達式(<前面能夠匹配任何空白字符,包括空格、製表符、換頁符等等)緩存
簡單測試下:
var str = ' <div id=top></div>'; var match = rquickExpr.exec(str); console.log(match) //[" <div id=top></div>", "<div id=top></div>", undefined, index: 0, input: " <div id=top></div>"]
var str = '[?\f\n\r\t\v]<div id=top></div>';
var str = '#test'; var match = rquickExpr.exec(str); console.log(match) //["#test", undefined, "test", index: 0, input: "#test"]
jQuery選擇器接口
API
jQuery是總入口,選擇器支持9種方式的處理
1.$(document) 2.$(‘<div>’) 3.$(‘div’) 4.$(‘#test’) 5.$(function(){}) 6.$("input:radio", document.forms[0]); 7.$(‘input’, $(‘div’)) 8.$() 9.$("<div>", { "class": "test", text: "Click me!", click: function(){ $(this).toggleClass("test"); } }).appendTo("body"); 10$($(‘.test’))
jQuery這個選擇器重構了幾回後,如今邏輯結構至關的清晰了,一看大概就知道
不能不得說jQuery的反模式,非職責單一深受開發者喜歡,一個接口承載的職責越多內部處理就越複雜了
jQuery查詢的的對象是dom元素,查詢到目標元素後,如何存儲?
本質上講jQuery.fn.init構建的出來的對象,就是jQuery對象
init: function( selector, context, rootjQuery ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Handle HTML strings if ( typeof selector === "string" ) { // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); },
源碼縮進後的結構:
匹配模式一:$("#id")
1. 進入字符串處理
if ( typeof selector === "string" ) {
2. 發現不是 "<"開始,">"結尾 $('<p id="test">My <em>new</em> text</p>')這種的狀況
若是selector是html標籤組成的話,直接match = [ null, selector, null ];
而不用正則檢查
if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
3. 不然的話須要match = rquickExpr.exec( selector )
match = rquickExpr.exec( selector );
4. 匹配的html或確保沒有上下文指定爲# id
if ( match && (match[1] || !context) ) {
5. match[1]存在,處理$(html) -> $(array),,也就是處理的是html方式
if ( match[1] ) {
6. 處理ID
elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this;
至此本次檢索完畢!
能夠看到
this就是jQuery工廠化後返回的對象
匹配模式二:<htmltag>
重複的地方跳過直接看處理接口
if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; // scripts is true for back-compat jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else {
傳入上下文:
context && context.nodeType ? context.ownerDocument || context : document
ownerDocument和 documentElement的區別
具體請看API手冊
jQuery.merge( first, second ) 合併兩個數組內容到第一個數組。
jQuery.parseHTML
使用原生的DOM元素的建立函數將字符串轉換爲一組DOM元素,而後,能夠插入到文檔中。
str = "hello, <b>my name is</b> jQuery.",
html = $.parseHTML( str ),
源碼:
parseHTML: function( data, context, keepScripts ) { if ( !data || typeof data !== "string" ) { return null; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } context = context || document; var parsed = rsingleTag.exec( data ), scripts = !keepScripts && []; // Single tag if ( parsed ) { return [ context.createElement( parsed[1] ) ]; } parsed = jQuery.buildFragment( [ data ], context, scripts ); if ( scripts ) { jQuery( scripts ).remove(); } return jQuery.merge( [], parsed.childNodes ); },
匹配一個獨立的標籤
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
這樣若是沒有任何屬性和子節點的字符串(好比'<html></html>'或者'<div></div>'這樣)會經過正則的匹配,當經過正則的匹配後則會經過傳入的上下文直接建立一個節點:
只是單一的標籤:
if ( parsed ) { return [ context.createElement( parsed[1] ) ]; }
而未經過節點的字符串,則經過建立一個div節點,將字符串置入div的innerHTML:
parsed = jQuery.buildFragment( [ data ], context, scripts );
它會把傳入的複雜的html轉爲文檔碎片而且存儲在jQuery.fragments這個對象裏。這裏要提一下,document.createDocumentFragment()是至關好用的,能夠減小對dom的操做.
建立一個文檔碎片DocumentFragment
當一個HTML比一個沒有屬性的簡單標籤複雜的時候,實際上,建立元素的處理是利用了瀏覽器的innerHTML
機制。
1 tmp = tmp || fragment.appendChild( context.createElement("div") ); 2 3 // Deserialize a standard representation 4 tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase(); 5 wrap = wrapMap[ tag ] || wrapMap._default; 6 tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
$('<img />')
or $('<a></a>')
,jQuery將使用javasrcipt原生的 createElement()
函數建立這個元素。 .innerHTML
屬性來解析傳遞的HTML並將其插入到當前文檔中。在此過程當中,一些瀏覽器過濾掉某些元素,如<html>
, <title>
, 或 <head>
的元素。其結果是,被插入元素可能不是傳入的原始的字符串。 href
屬性爲絕對URL路徑,和Internet Explorer第9版以前,不增長一個單獨的兼容層的狀況下,將沒法正確處理HTML5元素。
若是第一個參數(HTML字符串)爲一個空的單標籤,且第二個參數context爲一個非空純對象
var jqHTML = $('<div></div>', { class: 'css-class', data-name: 'data-val' }); console.log(jqHTML.attr['class']); //css-class console.log(jqHTML.attr['data-name']); //data-val
匹配模式三:$(.className)
若是第一個參數是一個.className,jQuery對象中擁有class名爲className的標籤元素,並增長一個屬性值爲參數字符串、document的selector、context屬性
return jQuery(document).find(className);
匹配模式四:$(.className, context)
若是第一個參數是.className,第二個參數是一個上下文對象(能夠是.className(等同於處理$(.className .className)),jQuery對象或dom節點),
jQuery對象包含第二個參數上下文對象中擁有class名爲className的後代節點元素,並增長一個context和selector屬性
return jQuery(context).find(className);
匹配模式五:$(jQuery對象)
若是第一個參數是jQuery對象,上面已經分析過若是在查詢dom時,參數是一個#加元素id,返回的jQuery對象會增長一個屬性值爲參數字符串、document的selector、context屬性
var jq = $('#container'); console.log(jq.selector); // #container console.log(jq.context); // document
那麼當出現$($('#container'))該如何處理呢?一樣的,返回的jQuery對象同狀況
var jq2 = $($('#container')); console.log(jq2.selector); // #container console.log(jq2.context); // document
等等..................
jQuery 構造器
因而可知,從本質上來講,構建的jQuery對象,其實不只僅只是dom,還有不少附加的元素,用數組的方式存儲,固然各類組合有不同,可是存儲的方式是同樣的
總的來講分2大類:
CSS選擇器是經過jQuery.find(selector)函數完成的,經過它能夠分析選擇器字符串,並在DOM文檔樹中查找符合語法的元素集合
選擇器這章有點亂,東西太多了,不能一一陳列 , 後期在慢慢整理