轉自:http://www.javashuo.com/article/p-ghyumuxz-ha.htmlhtml
打開jQuery源碼,首先你會看到這樣的代碼結構:jquery
(function(window,undefined ){ // })();
這是一個自調用匿名函數。什麼東東呢?在第一個括號內,建立一個匿名函數;第二個括號,當即執行ajax
爲何要建立這樣一個「自調用匿名函數」呢?
經過定義一個匿名函數,建立了一個「私有」的命名空間,該命名空間的變量和方法,不會破壞全局的命名空間。這點很是有用也是一個JS框架必須支持的功能,jQuery被應用在成千上萬的JavaScript程序中,必須確保jQuery建立的變量不能和導入他的程序所使用的變量發生衝突。
接下來看看在 自調用匿名函數 中都實現了什麼功能,按照代碼順序排列:json
(function( window, undefined ) { // 構造jQuery對象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); } // 工具函數 Utilities // 異步隊列 Deferred // 瀏覽器測試 Support // 數據緩存 Data // 隊列 queue // 屬性操做 Attribute // 事件處理 Event // 選擇器 Sizzle // DOM遍歷 // DOM操做 // CSS操做 // 異步請求 Ajax // 動畫 FX // 座標和大小 window.jQuery = window.$ = jQuery; })(window);
匿名函數從語法上叫函數直接量,JavaScript語法須要包圍匿名函數的括號,事實上自調用匿名函數有兩種寫法:瀏覽器
(function() { console.info( this ); console.info( arguments ); }( window ) ); (function() { console.info( this ); console.info( arguments ); })( window );
常用的方法以下格式:緩存
(function($, owner){ //登陸 owner.login = function(info,callback){ //code }; //註冊 owner.reg = function(name,callback){ //code }; }(jQuery, window.app = {})); 調用: app.login(info,function(){}); app.reg(name,function(){})
爲何要傳入window呢?閉包
經過傳入window變量,使得window由全局變量變爲局部變量,當在jQuery代碼塊中訪問window時,不須要將做用域鏈回退到頂層做用域,這樣能夠更快的訪問window;這還不是關鍵所在,更重要的是,將window做爲參數傳入,能夠在壓縮代碼時進行優化,看看jquery-1.6.1.min.js: (function(a,b){})(window); // window 被優化爲 a
經過以上的介紹,咱們大概瞭解經過()可使得一個函數表達式當即執行。app
匿名函數做爲一個「容器」,「容器」內部能夠訪問外部的變量,而外部環境不能訪問「容器」內部的變量,
因此 ( function(){…} )() 內部定義的變量不會和外部的變量發生衝突,俗稱「匿名包裹器」或「命名空間」。框架
(function () { // ... 全部的變量和function都在這裏聲明,而且做用域也只能在這個匿名閉包裏 // ...可是這裏的代碼依然能夠訪問外部全局的對象 }()); 同下面 (function () {/* 內部代碼 */})();
通俗的講,()就是用來求值的,所以這個()任什麼時候候都不能爲空,由於它是要計算的。函數解析它只會解析到 {}爲止,不會解析到 ()的。
把表達式放在()中會返回表達式的值;
把函數放在()中會返回函數自己;(function(){}());
若是()緊跟在函數後面,就是表示在調用函數,即對函數求值:(function(){})();異步
(function() { //自執行函數中的內部變量,外部是沒法訪問的 var name = 'kevin'; })( window ); name //undefined,沒法獲取name的值
代碼在運行過程當中,會優先解析 【巳聲明的函數】;
而函數表達式是當執行到它時,纔會解析;
匿名函數是不會單獨寫的,所以它的執行是須要其它函數的調用,一般看到的匿名函數,都是看成參數被傳遞的。而當即執行函數它自己就是個匿名函數,
js代碼執行的順序: //巳聲明的函數 function test(){} //匿名函數 function (){} //函數表達式 var test = function(){} //當即執行函數 (function(){})();
當即執行函數配合閉包,在模塊化中的應用,其中要明白幾個點:
一、要在函數體後面加括號就能當即調用,則這個函數必須是函數表達式,不能是函數聲明;
二、當即執行函數能夠看成是一個私有做用域,做用域內部能夠訪問外部的變量,而外部環境是不能訪問做用域內部的變量的,所以,當即執行函數是一個封閉的做用域,不會和外部做用域起衝突。
JQuery使用的就是這種方法,將JQuery代碼包裹在( function (window,undefined){…jquery代碼…} (window)中,在全局做用域中調用JQuery代碼時,能夠達到保護JQuery內部變量的做用。
三、Module模式,是自執行函數的高級模式,能夠很是方便的在各個匿名閉包中以全局對象調用閉包函數。有興趣能夠查看:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html Module 模式爲:
a.建立一個當即調用的匿名函數表達式
b.return一個變量,其中這個變量裏包含你要暴露的東西
c.返回的這個變量將賦值給window
(function () { var i = 0; return { get: function () { return i; }, set: function (val) { i = val; }, increment: function () { return ++i; } }; } (window)); // window做爲一個帶有多個屬性的全局對象,上面的代碼對於屬性的體現實際上是方法,它能夠這樣調用: window.get(); // 0 window.set(3); window.increment(); // 4 window.increment(); // 5 window.i; // undefined 由於i不是返回對象的屬性 i; // 引用錯誤: i 沒有定義(由於i只存在於閉包)
/******上面就是關於自調用匿名函數的解析,那麼這樣的函數它是怎麼被調用的呢?*******/
/******下面是關於全局變量的調用,也就是匿名閉包函數的調用*******/
再次搬出Module模式,有興趣能夠查看:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html
Module 模式,也就是匿名閉包的建立與調用:
a.建立一個當即調用的匿名函數表達式
b.return一個變量,其中這個變量裏包含你要暴露的東西
c.返回的這個變量將賦值給window
window(或者是任意一個全局對象)做爲一個帶有多個屬性的全局對象,也能夠把window當成一個參數,以對象的方式,在其它函數中實現調用。用下面的例子說明:
(function ($, YAHOO) { // 這裏,咱們的代碼就可使用全局的jQuery對象了,YAHOO也是同樣 $.aa = function(){ //code } } (jQuery, YAHOO)); //調用 jQuery.aa();
下面是一個標準的Module模式,經過匿名函數的返回值來返回這個全局變量:
var blogModule = (function () { var my = {}, privateName = "博客園"; function privateAddTopic(data) { // 這裏是內部處理代碼 } my.Name = privateName; my.AddTopic = function (data) { privateAddTopic(data); }; return my; } ()); //調用 blogModule.my();
在一些大型項目裏,將一個功能分離成多個文件是很是重要的,由於能夠多人合做易於開發。再回頭看看上面的全局參數導入例子,咱們可否把blogModule自身傳進去呢?答案是確定的,咱們先將blogModule傳進去,添加一個函數屬性,而後再返回就達到了咱們所說的目的:
var blogModule = (function (my) { my.AddPhoto = function () { //添加內部代碼 }; return my; } (blogModule || {})); 或 (function (my){ my.AddPhoto = function () { //添加內部代碼 }; return my; })(blogModule || {})); //調用 blogModule.AddPhoto();
那麼,多個自執行函數間是怎麼調用的呢?
(function(owner) { //第一個匿名閉包 owner.debug = true; //Ajax相關參數配置 owner.ajax = { timeout: 10000, type: 'post', dataType: 'json', }; })(window.C = {}));
若是第二個函數想調用 全局變量爲C中的 對象呢?要怎麼寫?
(function($, owner) { //這裏調用上面全局變量爲C 中的對象呢 if(!C.debug) return false; var url = 'aaa.html'; mui.ajax({ url: url, dataType: C.ajax.dataType, type: C.ajax.type, }); })(mui, window.app = {});
再舉個例子,一樣的,不一樣自執行閉包函數間的調用方法:
(function($, owner) { //獲取語言閉包 owner.getLanguage = function() { var language = localStorage.getItem(C.state.field.language); if(typeof language == "undefined" || language === null || language == '') { var currentLang = navigator.language; if(!currentLang) currentLang = navigator.browserLanguage; language = currentLang.toLowerCase(); language = language.replace(/-/g, '_'); if(language != 'en_us' && language != 'zh_cn') language = 'en_us'; localStorage.setItem(C.state.field.language, language); }
//在上面的解析中有說過,Module模式,return 一個變量,這個變量就是要爆露的東西。經過這個函數的全局變量,這個 language 能夠在任何地方調用 //return一個變量,其中這個變量裏包含你要暴露的東西 //全局調用 storage.language return language; }; })(mui, window.storage = {})); (function($, owner) { owner.language = {}; owner.preload = function(settings){ var defaults = { name: 'i18n', language: '', path: '/', cache: true, encoding: 'UTF-8', autoReplace: true, success: null, error: null, }; settings = $.extend(defaults, settings); if(settings.language === null || settings.language == '') { //全局調用 storage.language settings.language = storage.getLanguage(); } } })(mui, window.i18n = {});
因此 匿名閉包的調用規則是這樣的,當即執行(最後一個括號) (window),若是把window做爲一個參數進行傳遞,那麼就把它以對象的方式,在其它函數中實現全局調用。
若是有錯誤,請你們指正,謝謝!