jQuery.fn.extend({
/*
可以看出這是一個函數稱為addClass的插件方法。
*/
addClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue,
i = 0,
/*
this表示選擇器選擇的準備添加class的jQuery對象,len是該jQuery對象數組的長度。
*/
len = this.length,
//在有一個操作數不是布林值的情況下,&&操作就不一定會傳回布林值,此時,它遵循下列規則:
//1.如果第一個操作數不是布林類型,則傳回第二個操作數;
//2.如果第二個操作數不是布林類型,則只有在第一個操作數的求值結果為true,的情況下才會傳回該物件;
//3.如果兩個操作數都不是布林類型,則傳回第二個操作數;
//4.如果有一個運算元是null,則回傳null;
//5.如果有一個操作數是NaN,則回傳NaN;
//6.如果有一個運算元是undefined,則回傳undefined 。
//情況1:如果value是null,符合規則4,回傳null,即proceed值為null;
//情況2:如果value是undefine,符合規則6,回傳undefined,即proceed值為undefined;
//情況3:如果value是NaN,符合規則5,則回傳NaN,即proceed值為NaN;
//情況4:如果value是數字類型,回傳false;
//情況5:如果value是布林型別,回傳false;
//情況7:如果value是Array,Object,Function類型,符合規則2,但typeof value === 'string'是false,所以返回該對象,則回傳false。
//情況8:如果value是字串類型,符合規則2,則回傳value。
//因此,這句話只能判斷value是不是字串型,並且傳回這個字串賦值給proceed。其他任何類型最後都會回傳false,或是可被隱式轉換為false的類型。
proceed = typeof value === "string" && value;
//由於上面只能判斷是否是字串類型,下面這句話是判斷value是否為Function類型。使用了jQuery的全域函數isFunction判斷,就是$.isFunction()。
if ( jQuery.isFunction( value ) ) {
//如果value是Function類型,就進到這裡了。
//回傳jQuery對象,為了鍊式呼叫。
//這裡的this是你選擇器選擇的jQuery物件。
return this.each(function( j ) {
//開始迭代,這裡的this可不是jQuery物件了,是目前迭代的DOM對象,所以用jQuery(this)包裝了一下,成為一個jQuery對象,這樣就可以使用jQuery方法。 j表示每次遍歷的索引。傳遞一個用來設定類別名稱的有回傳值的value函數。 value這個函數每次call當前DOM為其執行對象,並傳入當前DOM索引值和類別名,value函數返回的值,由jQuery(this).addClass(返回值)再次呼叫addClass()方法。如果傳回的是字串,就執行另外一個if的分支。如果回傳的還是function,則繼續呼叫傳回的這個function。
jQuery( this ).addClass( value.call( this, j, this.className ) );
});
}
//之前得到proceed是字串,這裡判斷下是否為空字串,非空字串隱式轉換為true。空字串隱式轉換成false哦,那麼if語句區塊就不再執行了,程式跳到最後的return this,回傳這個jQuery物件就執行完了。
if ( proceed ) {
//proceed非空字串,開始執行if語句區塊。假定value是"show bd1"。
//rnotwhite是正規表示式(/S /g),意思是全域匹配非空白字元一次或更多次。
//(value || "")回傳"show bd1",很簡單。
//"show bd1".match((/S /g)) || []返回["show", "bd1"],match不知道什麼作用的同學快去查查吧。
classes = ( value || "" ).match( rnotwhite ) || [];
//現在classes是['show', 'bd1']一個你要加入類別名稱的陣列。
//下面開始遍歷,為每個DOM物件新增類別。
for ( ; i
//this是jQuery對象,elem是當前DOM對象。
elem = this[ i ];
/*
===運算子比&&運算子優先權高,先判斷DOM節點型別是否為元素節點。
rclass是正規表示式/[trnf]/g;
括號內的三目運算子表示,當前DOM節點是否已經有class,有的話,則將class中可能存在的製表符,換行符,回車符等替換為帶有一個空格的字串" " ,並且在當前class的前後各加一個空格;如果當前DOM節點還沒有class,則也是給一個帶一個空格的字串" "。最後變成
cur = elem.nodeType === 1 && "show bd1",這很熟悉啊,沒錯,依照最開始的那6個規則求值。
假設elem的節點型別是1,則 cur = true && " ",最後cur = "show bd1"。
如果elem的節點類型不是1,那麼 cur = false && " ",最後cur = false。
*/
cur = elem.nodeType === 1 && ( elem.className ?
( " " elem.className " " ).replace( rclass, " " ):" ");
//現在cur = " show bd1 ",進入if語句區塊執行。
if ( cur ) {
j = 0;
/*
classes為["show bd1"]
循環檢查要新增的類別是否已經存在與目前DOM元素已經有的類別裡。
如果沒有,則添加這個類別。
*/
while ( (clazz = classes[j ]) ) {
if ( cur.indexOf( " " clazz " " )
cur = clazz " ";
}
}
/*
最後用$.trim()將類別" show bd1 "兩頭的空格字元去掉。
檢查目前DOM元素的類別是否和拼接好的類別重複。避免不必要的重複添加相同類別的渲染。
*/
finalValue = jQuery.trim( cur );
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
}
}
}
//回傳這個jQuery對象,為了以後的鍊式呼叫。
return this;
}
});