對於jQuery的調用,咱們通常都會傳入參數html
html: <div></div> js: console.log($('<a />')); console.log($('div'));
仔細觀察返回的jQuery對象,感受像是對DOM節點進行封裝,並將其保存在jQuery上前端
//獲取到傳入的參數 var jQuery = function(selector, content) { return new jQuery.prototype.init(selector, content); } jQuery.prototype = { length: 0, init: function(selector, content) { content = content || document; var match; //match 用來保存selector; if(typeof selector === 'string') { // 判斷selector傳入的是一個html標籤; if(selector.charAt(0) === '<' && selector.charAt(selector.length-1) === '>' && selector.length >= 3 ) { match = [selector]; } if (match) { //建立一個jQuery對象。 } } } }
var reg = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; jQuery.extend({ parseHtml: function(data, content) { if (!data && typeof data != 'string') { return null; } var parse = reg.exec(data); return [content.createElement(parse[1])] //ok,這裏是將建立的一整個DOM節點保存在了數組中。 } })
if(match) { for(; i < match.length; i++) { var opt = match[i] this[i]=jQuery.parseHtml(opt, content)[i] } }
好了,如今咱們就能夠看下代碼的執行結果了。
ok,勉強能夠實現,但好像仍是有點不對的樣子, 讓咱們來看下jQuery的源碼。
能夠看出是調用了merge方法。跳轉到merge方法查看一下,通常來講,merge用於合併兩個數組, 也能夠用於將數組合並在有length屬性的對象上。jquery
// push.apply(_, arraylike) throws on ancient WebKit merge: function (first, second) { var len = +second.length, j = 0, i = first.length; for (; j < len; j++) { first[i++] = second[j]; } first.length = i; return first; }瞭解的區別後,咱們來優化代碼吧,
優化數組
var ele, i = 0; if(match) {} else { ele = document.querySelectorALL(selector); for(; i < ele.length; i++) { this[i] = ele[i] } this.length = ele.length; }
var rootjQuery; init = jQuery.fn.init = function(selector, context, root) { ... root = root || rootjQuery; if(typeof selector === 'string') { ... } else if (isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : selector(jQuery); } } rootjQuery = jQuery(document);
代碼能夠看出,其實js是能夠傳遞三個參數的,且root默認爲document,若是root.ready沒有初始話的話就馬上執行傳入的方法,不然調用root.ready方法。關於root,從代碼能夠看出,是個全局,那麼root.ready == jQuery.ready。promise
function completed() { document.removeEventListener('DOMContentLoaded',completed); window.removeEventListener('load', completed); jQuery.ready(); } if(document.readyState === 'complete' || (document.readyState !== "loading" && !document.documentElement.doScroll)) { window.setTimeout(jQuery.ready); } else { //dom加載完畢後,調用complate移除監聽事件。 document.addEventListener('DOMContentLoaded', complete); window.addEventListener('load', completed); }
jQuery.extend({ // DOM是否已經準備好要使用了,發生更改,則修改成true; isReady: false, //跟蹤就緒事件觸發前要等待的項目數計數器 readyWait: 1, ready: function(wait) { if(wait === true ? --jQuery.readyWait : jQuery.isReady) { return ; } //DOM節點已經準備好了 jQuery.isReady = true; if(wait !== true && --jQuery.readyWait > 0) { return } //若是有函數綁定,當即執行。 readyList.resolveWidth(document, [jQuery]) } })
從2.的時候能夠看出,在DOM節點加載完的時候,調用了一次ready,此時沒有傳入wait,ready中第一個判斷直接跳過,記錄jQuery.isReady = true,而後再看下一句執行,調用readyList.resolveWidth方法,此時DOM節點已經加載完畢,能夠執行綁定的函數了,
關於這個readyList是什麼,咱們在代碼中找一下看下,app
var readyList = jQuery.Deferred(); jQuery.fn.ready = function(fn) { ready.then(fn).catch(error) { jQuery.readyException(error); } return this; }
從以上代碼能夠看出,readyList是Deferred函數的返回值,且從下面的調用,能夠推測Defferred函數應該是個promise對象。對於Defferred函數,下次再仔細研究一下。
如今讓咱們來回顧整個函數流程:
上面的內容只是我本身的理解,若是有什麼不對的地方,但願你們幫忙指出啊!dom