標籤:
今天開啟JQuery源檔案(jquery-1.8.3), 看到JQuery的初始化過程是這樣的
(
function
( window, undefined ) {
// ....
})( window );
一開始看不懂這個寫法, 經過幾番搜尋終於明白它的用法以及為什麼這樣用了, 我們一步步來分析.
1, 首先我們簡化這個寫法
除去參數, 經過簡化後的寫法可以寫成
(
function
(){
console.log(
"Hello World"
);
})();
後面都使用這個寫法作為樣本.
2, 函式宣告與函數運算式
網上有許多介紹建立JavaScript函數的文章告訴我們建立JavaScript函數有兩種方式: 函式宣告與函數運算式.
//function definition (also called function declaration)
function
func1() {
console.log(
"Hello World1"
);
}
func1();
// function expression
var
func2 =
function
(){
console.log(
"Hello World2"
);
};
func2();
第一種方式"函數定義"是標準的函數定義方式, 對函數func1的調用可以出現在函數定義之前;
第二種被稱為"函數運算式", 與函數定義不同的是對函數func2的調用必須出現在變數func2之後的, 因為變數func2本質上是一個指向函數對象的變數, 這與我們定義普通變數的方式本質上是一樣的; 另外一點是通過函數運算式建立的函數名可以不寫, 我們稱之為匿名函數.比如
var a = 10;var f = function(){//code...};
f(); // 調用函數f
這裡我們將變數f指向賦值運算子右邊所建立的匿名函數, 然後就可以通過f()直接去調用這個匿名函數了, 那麼, 問題來了, 我們是不是可以直接在建立好匿名函數之後就立即調用而不去多此一舉賦值給變數f呢?
我們試試
function(){console.log("Hello World3");}();// output: Uncaught SyntaxError: Unexpected token (
很遺憾報錯, 為什麼會這樣呢? 這時我們再回過頭來看看函數定義與函數運算式的文法區別.
函數定義: function funcName(){//code...} funcName();
函數運算式: function [funcName](){//code...} funcName();
兩者的文法差別很小, 因此當JavaScript解譯器解釋到function關鍵字是是把這段代碼當做函數定義呢還是函數運算式呢? 根據JavaScript文法, 以function開始的語句會被當做函數定義, 而函數定義是必須要有函數名的, 並且通過函數名來執行函數, 但是顯然function(){};()是不符合這個文法規範的, 這也就解釋了為什麼會報錯. 所以, 任何可以使得JavaScript解譯器把這一語句解釋為函數運算式的方法都應該能讓這一句代碼成功執行. 那麼問題又來了: 如何?這一目的呢?
var a = 1;
這就是一個簡單的運算式(expression), 因此我們想到可以在這一語句前面加上一個合適的運算子, 在這裡由於運算子右邊只能有一個函數對象運算元(JavaScript語句), 所以我們應該用運算元可以為對象的(一元)運算子, 我們來試試各種運算子.
! function(){console.log("Hello World2");}(); //Hello World2+ function(){console.log("Hello World3");}(); //Hello World3- function(){console.log("Hello World4");}(); //Hello World4delete function(){console.log("Hello World5");}(); //Hello World5void function(){console.log("Hello World6");}(); //Hello World6
這些運算子都能實現我們的目的, 即讓JavaScript解譯器以建立函數運算式的方式建立這個函數. 至於具體使用哪一個運算子可以自己決定, 不過很明顯我們希望用最簡潔的方式. 在實踐中一些大牛傾向於使用"!", 這一點在stackoverflow中有非常多的討論. http://stackoverflow.com/questions/3755606/what-does-the-exclamation-mark-do-before-the-function
3, 圓括弧Parenthesis
另外一種更常用的寫法, 也就是JQuery的用法, 是用圓括弧作為分組操作符來讓該執行函數語句被強制解釋為以函數運算式的方式來建立這個匿名函數.
// 第一種寫法 ()分組運算子的內部代碼只能是運算式, 這裡將以函數運算式的方式建立並返回匿名函數(function(){/* code... */ };)();// 第二種寫法 直接將最頂層的括弧內部當做運算式建立並運行該匿名函數(function(){...}(););
(function(){console.log("Hello World6");})(); //Hello World6
(function(){console.log("Hello World6");}()); //Hello World6
4, 結論
分析下來, 其實這樣寫的目的很簡單: 就是定義一個匿名函數並執行. 至於為什麼這樣寫, 這是利用閉包closure的特性來初始化全域變數, 將這些全域變數的scope控制在匿名函數內部. 至於閉包, 下次再扯吧, 先下班了.
參考資料
1, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
2, http://www.zhihu.com/question/20292224
3, http://www.zhcexo.com/round-brackets-in-javascript/
4, http://swordair.com/function-and-exclamation-mark/
JavaScript自運行函數(function(){})()的理解