寫在前面:本<JQuery源碼解析>系列是基於一些前輩們的文章進行進一步的分析、細化、修改而寫出來的,在這邊感謝那些慷慨提供科普文檔的技術大拿們。
要查閱JQ的源文件請下載開發版的JQ.js文檔,下載地址:http://jquery.com/download/ 注意選擇其中的development版本進行下載,如下圖所示

開發版本的JQ.js屬於非壓縮的源文件,方便我們閱讀和分析其代碼。 下載完用Dreamweaver或其它代碼編輯器打開查閱即可。我們今后分析的代碼也是基於1.11.0版本的JQ源代碼。
第一眼看JQ的源代碼或許會感到混亂和沒頭緒,特別是會卡在36行的代碼那里遲遲找不到“function( window, noGlobal ) {”的后半段終點:
// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { /* 很容易看卡在這里,不知道function( window, noGlobal )到哪里才結束 */
不過現在可以直接告訴你,function( window, noGlobal )可謂是貫穿后面代碼的全部部分,屬於JQ總體架構的一部分。JQ的主體代碼如下:
(function( global, factory ) {
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper window is present,
// execute the factory and get jQuery
// For environments that do not inherently posses a window with a document
// (such as Node.js), expose a jQuery-making factory as module.exports
// This accentuates the need for the creation of a real window
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { /* 剛剛說的容易看卡住的地方 */
//說白了這里就是寫各種JQ功能函數的地方,大概有一萬多行
}));
我們把上面這個主架構函數再簡化成這樣:
(function( global, factory ) {
.....
}(參數1,參數2)); //參數1就是那個“typeof window !== "undefined" ? window : this”,參數2就是那個“function( window, noGlobal ){..這里有一萬多行哦..}”
這種函數形式我們稱之為匿名函數,也就是沒有函數名的函數。介紹匿名函數之前我們先來看看函數的定義方式有哪些:
第一種,也是常規的一種:
function abc(x){
alert (2 * x);
}
第二種:這種方法使用了Function構造函數,把參數列表和函數體都作為字符串,很不方便,不建議使用:
var abc = new Function('x', 'alert (2 * x);');
第三種:等號右側先定義好函數,再賦給等號左側的變量。
var abc = function(x) { alert( 2* x ) ; }
這里你需要知道的一點是,等號右邊的函數,就是匿名函數了。我們可以用一個變量指向它,並稱此變量為該函數的“函數字面量”(注意不是函數名字,匿名函數的不會偶函數名的),像var aa=function(x) { alert( 2* x ) ; } ,則“aa”就是該函數的函數字面量。我們可以通過 aa(實參) 來執行該函數。
匿名函數還有另一種定義形式:
(function(x){
alert(2* x);
})(3);
/* 也可以寫做
(function(x){
alert(2* x);
}(3));
*/
這種形式的函數沒有函數名,卻有調用參數的專屬括號(像上面的是“(3)”,表示把3作為參數來強制調用賦值給x,最終alert出6)。
需要知道的是,像
(function(參數){...;}(調用參數)); //“參數”和“調用參數”都是可有可無、數量可變的
這種形式的匿名函數,都是會立即執行的,無須寫什么調用函數的代碼就會自動執行。你大可以寫個頁面文件,加上
<script language="javascript">
(function(x){
alert( 1+x );
})(2);
</script>
然后運行該頁面,立刻會彈出“3”的窗口。
了解了匿名函數,那么我們回看JQ源碼:

可見JQ源碼就是一個匿名函數
1 (function( global, factory ) { 2 3 ... //這里是為了兼容nodejs等一些其它的js框架; 4 5 }(a,b))
其中形參global的實參a是一個三目運算符 typeof window !== "undefined" ? window : this 用於判斷當前執行環境是否支持window類型,是的話返回window,否則返回this
形參factory的實參b則是一個函數,里面包含了一萬多行的JQ功能函數 function( window, noGlobal ) { ...... }
既然這個外部匿名函數的參數的值我們都清楚了,那么來看下這個匿名函數又是啥作用的?(光看JQ自帶的英文注釋我們可以大致知道它是為了兼容node.js、sea-JS等符合CommonJS規范或類CommonJS規范的js框架)
首先我們看這行判斷語句: if ( typeof module === "object" && typeof module.exports === "object" )
玩過node.js的朋友自然會知道module.export和export是node.js中用來創建模塊的方法,那么就好理解了,若此條件成立,則要執行下面語句來兼容node.js(說白了就是利用形參factory做中間人,來把JQ的各個功能模塊用node.js創建模塊的方法創建起來)
{
module.exports = global.document ? //三目運算符,先判斷當前環境是否支持window.document屬性
//(注意我們上面提到過形參global的實參是window)
factory( global, true ) : //支持的話就好辦啦,只要咱用常規的瀏覽器一般都是支持的,那就直接module.exports = factory( global, true ),
//把JQ后面那一萬多行的功能函數擴展到node.js里面。(注意我們上面提到過形參factory的實參是實現JQ各種功能的一個外部函數)
function( w ) { //如果當前環境不支持window.document屬性,那就寫個函數扔個Error說這環境不適用JQ,但依舊返回JQ的功能函數(但大部分估計是不能用的了)
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
}
嗯,這樣就兼容了node.js咯,那么如果咱沒有用node.js這種CommonJS規范的框架,也就是說條件if ( typeof module === "object" && typeof module.exports === "object" )不成立。那就直接執行后面else里的部分:
factory( global );
也就是直接引入JQ那一萬多行的功能函數即可。
話說也寫了不少字,第一部分先到這里吧 :)

