Java程式員的JavaScript學習筆記(9—— jQuery工具方法)
計劃按如下順序完成這篇筆記:
1. 理念。
2. 屬性複製和繼承。
3. this/call/apply。
4. 閉包/getter/setter。
5. prototype。
6. 物件導向類比。
7. jQuery基本機制。
8. jQuery選取器。
9. jQuery工具方法。
10. jQuery-在“類”層面擴充。
11. jQuery-在“對象”層面擴充。
12. jQuery-延伸選取器。
13. jQuery UI。
14. 擴充jQuery UI。
這是筆記的第9篇,從jQuery源碼的角度,聊聊jQuery的工具方法。
作者部落格:http://blog.csdn.net/stationxp
作者微博:http://weibo.com/liuhailong2008
轉載請取得作者同意
1、先看幾個工具方法如何使用:
var t = $.trim(' >>_<< ');var ps = $.param({x:15,y:16});jQuery.type(function(){}) === "function"
jQuery的工具方法,相對jQuery本身,是相對獨立的。
2、這些方法是如何定義的呢?
我們猜測一下,試試看。
我們知道jQuery(即$)是全域變數,這些方法應該是jQuery變數的屬性。
我們可以通過如下文法添加:
jQuery.xx = function(a,b){ return a + b;}// tryvar r1 = jQuery.xx(2,3);console.log(r1);// output : 5var r2 = $.xx(3,3);console.log(r2);// output : 6上面代碼,說明$和jQuery引用了相同變數,他們是一樣一樣的。
這些代碼達到了定義工具方法的目的,但jQuery是這樣做的嗎?
3、
查看jQuery原始碼,jQuery通過如下文法定義工具方法:
jQuery.extend({ noConflict: function( deep ) { if ( window.$ === jQuery ) {window.$ = _$; } if ( deep && window.jQuery === jQuery ) {window.jQuery = _jQuery; } return jQuery; } //, ... });
extend方法定義如下:
//定義了一個方法,賦值給jQuery.fn.extend和jQuery.extend.//jQuery.fn.extend和jQuery.extend雖然定義相同,但調用者不同,從而方法中this指向不同,從而實現的功能不同。//沒有顯式聲明參數,而是通過arguments動態獲得,從而支援更豐富的功能。jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, // 將第一個參數作為目標對象 target = arguments[0] || {}, // i初始化為1,暫時還不知道做什麼用 i = 1, // length表示參數的個數 length = arguments.length, // deep 應該表示是否深層拷貝,預設為否。 deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { //如果第一個參數是布爾類型,則第二個參數為目標對象 //形如:jQuery.extend(false,UiObject,...); deep = target; target = arguments[1] || {}; // i 是當前參數的數組下標,從0開始 i = 2; } //目標對象是能是函數或者對象,傳入其他參數,target預設為空白對象。 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // 目前,如果第一個參數不是布爾,i值為1,length也為1,即extend(obj)的情況 // 如果是布爾,i值為2,length也為2,即extend(true,obj)的情況 // 總之,沒有傳入 src if ( length === i ) { // jQuery 把傳入參數收了 // 參數下標重新指向傳入的這個參數,即此時指向了src target = this; --i; } // 現在i指向了target後面某個參數,應該是src // 把後面傳入的每個參數都當src for ( ;i < length; i++ ) { // Only deal with non-null/undefined values // 這樣過濾非空啊,如果是undefined會被過濾掉嗎? // 不做 typeof src 是否 "object"、"function"的判斷嗎? if ( (options = arguments[ i ]) != null ) { // 賦值給變數 options // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // 思慮萬全:如果已經是自家人,就別嘚瑟了 // Prevent never-ending loop if ( target === copy ) { continue; } // 對普通對象和數組,考慮遞迴深層拷貝 // 問題來了,什麼叫普通對象? function 算不算? if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { // 設下次迴圈的預設值 copyIsArray = false; // 目標對象如果原來有這個屬性,但不是數群組類型,就被幹掉了 clone = src && jQuery.isArray(src) ? src : []; } else { // 目標對象如果原來沒有這個屬性或者不是普通對象,預設為{}。 // 如果已經有了而且是普通對象,那就用它了 clone = src && jQuery.isPlainObject(src) ? src : {}; } //遞迴調 //如果寫成 jQuery.extend( deep, clone, copy ) 是否一樣 // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values // 對 undefined 和 != null 還要加深理解 } else if ( copy !== undefined ) { // 臨門一腳,如果是 jQuery.extend({xx,xfasf});的情況,擴充的是this,即調用者。 // 對target直接修改,也沒建立clone啥的 target[ name ] = copy; } } } } // Return the modified object return target;};
4、再看看jQuery.isPlainObject如何定義的
isPlainObject: function( obj ) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well // 翻譯: // 必須是一個對象。 // 因為IE的緣故,我們還必須檢查是否有constructor屬性。 // 確認別放過Dom節點和window對象,他們不是普通對象。 if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { // 以下不通過空、undefined、0、jQuery.type返回不是object的、dom對象、window對象 // 所以 typeof 為string、number、function、正則、date的都不成 return false; } try { // Not own constructor property must be Object if ( obj.constructor &&!core_hasOwn.call(obj, "constructor") &&!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {return false; } } catch ( e ) { // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // 枚舉的時候,自有屬性會先被枚舉出來,如果最後一個屬性是自有屬性,那麼所有屬性都是自有屬性。 // 簡而言之,不能有繼承來的,可以被枚舉到的屬性,你妹!為什麼有這樣的要求? // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for ( key in obj ) {} return key === undefined || core_hasOwn.call( obj, key ); }
5、
試試看
jQuery.extend({ sayHi:function(you){ console.log('hi',you); }});$.sayHi('Stanley'); // output : hi Stanley
還不錯!如果想覆蓋jQuery已有的方法呢?
jQuery.extend({ isPlainObject:function(obj){return 'baby, I am not sure.'; }});var r = $.isPlainObject({});console.log(r); // output : baby, I am not sure.
這...好吧,可以得逞。想想別的辦法吧,不能這樣。
再玩兒:
jQuery.extend({ extend:function(obj){ // do nothing return 'who am i?'; }});var r = $.extend({x:function(){ alert();}});console.log(r); // output : who am i?jQuery.x();// error : function not be defined
又得逞,這樣,再沒人可以通過extend擴充jQuery的方法了。
但可以有更直接的方法。