欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery源碼分析之init的詳細(xì)介紹

 更新時(shí)間:2017年02月13日 10:18:56   作者:漁人  
在閱讀本文之前,相信大家對(duì) jQuery 的總體架構(gòu)已經(jīng)了解了,這一篇文章主要來(lái)介紹 jQuery 的入口函數(shù) jQuery.fn.init。有需要的朋友可以參考借鑒,下面來(lái)一起看看吧。

init 構(gòu)造器

由于這個(gè)函數(shù)直接和 jQuery() 的參數(shù)有關(guān),先來(lái)說下能接受什么樣的參數(shù)。

源碼中接受 3 個(gè)參數(shù):

init: function (selector, context, root) {
 ...
}
  1. jQuery() ,空參數(shù),這個(gè)會(huì)直接返回一個(gè)空的 jQuery 對(duì)象,return this。
  2. jQuery( selector [, context ] ) ,這是一個(gè)標(biāo)準(zhǔn)且常用法,selector 表示一個(gè) css 選擇器,這個(gè)選擇器通常是一個(gè)字符串,#id 或者 .class 等,context 表示選擇范圍,即限定作用,可為 DOM,jQuery 對(duì)象。
  3. jQuery( element|elements ) ,用于將一個(gè) DOM 對(duì)象或 DOM 數(shù)組封裝成 jQuery 對(duì)象。
  4. jQuery( jQuery object|object ) ,會(huì)把普通的對(duì)象或 jQuery 對(duì)象包裝在 jQuery 對(duì)象中。
  5. jQuery( html [, ownerDocument ] ) ,這個(gè)方法用于將 html 字符串先轉(zhuǎn)成 DOM 對(duì)象后在生成 jQuery 對(duì)象。
  6. jQuery( html, attributes ) ,和上一個(gè)方法一樣,不過會(huì)將 attributes 中的方法和屬性綁定到生成的 html DOM 中,比如 class 等。
  7. jQuery( callback ) ,此方法接受一個(gè)回掉函數(shù),相當(dāng)于 window.onload 方法,只是相對(duì)于。

介紹完入口,就開始來(lái)看源碼。

init: function (selector, context, root) {
 var match, elem;

 // 處理: $(""), $(null), $(undefined), $(false)
 if (!selector) {
 return this;
 }
 // rootjQuery = jQuery( document );
 root = root || rootjQuery;

 // 處理 HTML 字符串情況,包括 $("<div>")、$("#id")、$(".class")
 if (typeof selector === "string") {

 //此部分拆分,留在后面講

 // HANDLE: $(DOMElement)
 } else if (selector.nodeType) {
 this[0] = selector;
 this.length = 1;
 return this;

 // HANDLE: $(function)
 } else if (jQuery.isFunction(selector)) {
 return root.ready !== undefined ? root.ready(selector) :

 // Execute immediately if ready is not present
 selector(jQuery);
 }

 return jQuery.makeArray(selector, this);
}

上面有幾點(diǎn)需要注意,root = root || rootjQuery; ,這個(gè)參數(shù)在前面介紹用法的時(shí)候,就沒有提及,這個(gè)表示 document,默認(rèn)的話是 rootjQuery,而 rootjQuery = jQuery( document ) 。

可以看出,對(duì)于處理 $(DOMElement) ,直接是把 jQuery 當(dāng)作一個(gè)數(shù)組,this[0] = DOMElement 。其實(shí),這要從 jQuery 的基本構(gòu)造講起,我們完成一個(gè) $('div.span') 之后,然后一個(gè) jQuery 對(duì)象(this),其中會(huì)得到一組(一個(gè))DOM 對(duì)象,jQuery 會(huì)把這組 DOM 對(duì)象當(dāng)作數(shù)組元素添加過來(lái),并給一個(gè) length。后面就像一些鏈?zhǔn)胶瘮?shù)操作的時(shí)候,若只能對(duì)一個(gè) DOM 操作,比如 width、height,就只對(duì)第一個(gè)元素操作,若可以對(duì)多個(gè) DOM 操作,則會(huì)對(duì)所有 DOM 進(jìn)行操作,比如 css()。

jQuery 大題思路如下,這是一個(gè)非常簡(jiǎn)單點(diǎn)實(shí)現(xiàn):

jQuery.prototype = {
 // 簡(jiǎn)單點(diǎn),假設(shè)此時(shí) selector 用 querySelectorAll
 init: function(selector){
 var ele = document.querySelectorAll(selector);
 // 把 this 當(dāng)作數(shù)組,每一項(xiàng)都是 DOM 對(duì)象
 for(var i = 0; i < ele.length; i++){
 this[i] = ele[i];
 }
 this.length = ele.length;
 return this;
 },
 //css 若只有一個(gè)對(duì)象,則取其第一個(gè) DOM 對(duì)象
 //若 css 有兩個(gè)參數(shù),則對(duì)每一個(gè) DOM 對(duì)象都設(shè)置 css
 css : function(attr,val){
 for(var i = 0; i < this.length; i++){
 if(val == undefined){
 if(typeof attr === 'object'){
 for(var key in attr){
 this.css(key, attr[key]);
 }
 }else if(typeof attr === 'string'){
 return getComputedStyle(this[i])[attr];
 }
 }else{
 this[i].style[attr] = val;
 }
 }
 },
}

所以對(duì)于 DOMElement 的處理,直接將 DOM 賦值給數(shù)組后,return this。

jQuery.makeArray 是一個(gè)綁定 數(shù)組的函數(shù),和上面的原理一樣,后面會(huì)談到。

在介紹下面的內(nèi)容之前,先來(lái)介紹一個(gè) jQuery 中一個(gè)識(shí)別 Html 字符串的正則表達(dá)式,

var rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;
rquickExpr.exec('<div>') //["<div>", "<div>", undefined]
rquickExpr.exec('<div></div>') //["<div></div>", "<div></div>", undefined]
rquickExpr.exec('#id') //["#id", undefined, "id"]
rquickExpr.exec('.class') //null

上面這一系列的正則表達(dá)式 exec,只是為了說明 rquickExpr 這個(gè)正則表達(dá)式執(zhí)行后的結(jié)果,首先,如果匹配到,結(jié)果數(shù)組的長(zhǎng)度是 3,如果匹配到 <div> 這種 html,數(shù)組的第三個(gè)元素是 underfined,如果匹配到 #id,數(shù)組的第二個(gè)元素是 underfined,如果匹配不到,則為 null。

另外還有一個(gè)正則表達(dá)式:

var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
rsingleTag.test('<div></div>') //true
rsingleTag.test('<div ></div>') //true
rsingleTag.test('<div class="cl"></div>') //false
rsingleTag.test('<div></ddiv>') //false

這個(gè)正則表達(dá)式主要是對(duì) html 的字符串進(jìn)行驗(yàn)證,達(dá)到不出差錯(cuò)的效果。在這里不多介紹 exec 和正則表達(dá)式了。

下面來(lái)看下重點(diǎn)的處理 HTMl 字符串的情況:

if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) {
 // 這個(gè)其實(shí)是強(qiáng)行構(gòu)造了匹配 html 的情況的數(shù)組
 match = [null, selector, null];

} else {
 match = rquickExpr.exec(selector);
}

// macth[1] 限定了 html,!context 對(duì) #id 處理
if (match && (match[1] || !context)) {

 // HANDLE: $(html) -> $(array)
 if (match[1]) {
 //排除 context 是 jQuery 對(duì)象情況
 context = context instanceof jQuery ? context[0] : context;

 // jQuery.merge 是專門針對(duì) jQuery 合并數(shù)組的方法
 // jQuery.parseHTML 是針對(duì) html 字符串轉(zhuǎn)換成 DOM 對(duì)象
 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) {

 // 此時(shí)的 match 非彼時(shí)的 match
 if (jQuery.isFunction(this[match])) {
 this[match](context[match]);

 // ...and otherwise set as attributes
 } else {
 this.attr(match, context[match]);
 }
 }
 }

 return this;

 // 處理 match(1) 為 underfined 但 !context 的情況
 } else {
 elem = document.getElementById(match[2]);

 if (elem) {

 // this[0] 返回一個(gè)標(biāo)準(zhǔn)的 jQuery 對(duì)象
 this[0] = elem;
 this.length = 1;
 }
 return this;
}
// 處理一般的情況,find 實(shí)際上上 Sizzle,jQuery 已經(jīng)將其包括進(jìn)來(lái),下章詳細(xì)介紹
// jQuery.find() 為 jQuery 的選擇器,性能良好
} else if (!context || context.jquery) {
 return (context || root).find(selector);
// 處理 !context 情況
} else {
 // 這里 constructor 其實(shí)是 指向 jQuery 的
 return this.constructor(context).find(selector);
}

關(guān)于 nodeType,這是 DOM 的一個(gè)屬性,詳情 Node.nodeType MDN。nodeType 的值一般是一個(gè)數(shù)字,比如 1 表示 DOM,3 表示文字等,也可以用這個(gè)值是否存在來(lái)判斷 DOM 元素,比如 context.nodeType。

整個(gè) init 函數(shù)等構(gòu)造邏輯,非常清晰,比如 (selector, context, root) 三個(gè)參數(shù),分別表示選擇的內(nèi)容,可能存在的的限制對(duì)象或 Object,而 root 則默認(rèn)的 jQuery(document) 。依舊采用 jQuery 常用的方式,對(duì)每一個(gè)變量的處理都非常的謹(jǐn)慎。

如果仔細(xì)看上面兩部分源代碼,我自己也加了注釋,應(yīng)該可以把整個(gè)過程給弄懂。

find 函數(shù)實(shí)際上是 Sizzle,已經(jīng)單獨(dú)出來(lái)一個(gè)項(xiàng)目,被在 jQuery 中直接使用,將在下章介紹 jQuery 中的 Sizzle 選擇器。通過源碼,可以發(fā)現(xiàn):

jQuery.find = function Sizzle(){...}
jQuery.fn.find = function(selector){
 ...
 //引用 jQuery.find
 jQuery.find()
 ...
}

衍生的函數(shù)

init 函數(shù)仍然調(diào)用了不少 jQuery 或 jQuery.fn 的函數(shù),下面來(lái)逐個(gè)分析。

jQuery.merge

這個(gè)函數(shù)通過名字,就知道它是用來(lái)干什么的,合并。

jQuery.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;
}

這樣子就可以對(duì)類似于數(shù)組且有 length 參數(shù)的類型進(jìn)行合并,我感覺主要還是為了方便對(duì) jQuery 對(duì)象的合并,因?yàn)?jQuery 對(duì)象就是有 length 的。

jQuery.parseHTML

這個(gè)函數(shù)也非常有意思,就是將一串 HTML 字符串轉(zhuǎn)成 DOM 對(duì)象。

首先函數(shù)接受三個(gè)參數(shù),第一個(gè)參數(shù) data 即為 html 字符串,第二個(gè)參數(shù)是 document 對(duì)象,但要考慮到瀏覽器的兼容性,第三個(gè)參數(shù) keepScripts 是為了刪除節(jié)點(diǎn)里所有的 script tags,但在 parseHTML 里面沒有體現(xiàn),主要還是給 buildFragment 當(dāng)作參數(shù)。

總之返回的對(duì)象,是一個(gè) DOM 數(shù)組或空數(shù)組。

jQuery.parseHTML = function (data, context, keepScripts) {
 if (typeof data !== "string") {
 return [];
 }
 // 平移參數(shù)
 if (typeof context === "boolean") {
 keepScripts = context;
 context = false;
 }

 var base, parsed, scripts;

 if (!context) {

 // 下面這段話的意思就是在 context 缺失的情況下,建立一個(gè) document 對(duì)象
 if (support.createHTMLDocument) {
 context = document.implementation.createHTMLDocument("");
 base = context.createElement("base");
 base.href = document.location.href;
 context.head.appendChild(base);
 } else {
 context = document;
 }
 }
 // 用來(lái)解析 parsed,比如對(duì) "<div></div>" 的處理結(jié)果 parsed:["<div></div>", "div"]
 // parsed[1] = "div"
 parsed = rsingleTag.exec(data);
 scripts = !keepScripts && [];

 // Single tag
 if (parsed) {
 return [context.createElement(parsed[1])];
 }
 // 見下方解釋
 parsed = buildFragment([data], context, scripts);

 if (scripts && scripts.length) {
 jQuery(scripts).remove();
 }

 return jQuery.merge([], parsed.childNodes);
}

buildFragment 函數(shù)主要是用來(lái)建立一個(gè)包含子節(jié)點(diǎn)的 fragment 對(duì)象,用于頻發(fā)操作的添加刪除節(jié)點(diǎn)。parsed = buildFragment([data], context, scripts);建立好一個(gè) fragment 對(duì)象,用 parsed.childNodes 來(lái)獲取這些 data 對(duì)應(yīng)的 HTML。

jQueyr.makeArray

jQuery 里面的函數(shù)調(diào)用,真的是一層接一層,雖然有時(shí)候光靠函數(shù)名,就能知道這函數(shù)的作用,但其中思考之邏輯還是挺參考意義的。

jQuery.makeArray = function (arr, results) {
 var ret = results || [];

 if (arr != null) {
 if (isArrayLike(Object(arr))) {
 jQuery.merge(ret, typeof arr === "string" ? [arr] : arr);
 } else {
 push.call(ret, arr);
 }
 }

 return ret;
}

makeArray 把左邊的數(shù)組或字符串并入到右邊的數(shù)組或一個(gè)新數(shù)組,其中又間接的引用 jQuery.merge 函數(shù)。

接下來(lái)是著 isArrayLike 函數(shù),可能需要考慮多方面的因素,比如兼容瀏覽器等,就有了下面這一長(zhǎng)串:

function isArrayLike(obj) {

 // Support: real iOS 8.2 only (not reproducible in simulator)
 // `in` check used to prevent JIT error (gh-2145)
 // hasOwn isn't used here due to false negatives
 // regarding Nodelist length in IE
 var length = !!obj && "length" in obj && obj.length,
 type = jQuery.type(obj);

 if (type === "function" || jQuery.isWindow(obj)) {
 return false;
 }

 return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj;
}

總結(jié)

這篇文章主要介紹了jQuery中比較重要的入口函數(shù),之后將會(huì)繼續(xù)講解 Sizzle,jQuery 中的選擇器。感興趣的朋友們請(qǐng)繼續(xù)關(guān)注腳本之家,謝謝大家的支持。

相關(guān)文章

最新評(píng)論