解釋封裝的匿名函數語法

摘要

您能解釋JavaScript中封裝的匿名函數的語法背後的緣由嗎? 爲何這樣作: (function(){})(); 但這不是: function(){}();express


我知道的

在JavaScript中,將建立一個命名函數,以下所示: ide

function twoPlusTwo(){
    alert(2 + 2);
}
twoPlusTwo();

您還能夠建立一個匿名函數並將其分配給變量: 模塊化

var twoPlusTwo = function(){
    alert(2 + 2);
};
twoPlusTwo();

您能夠經過建立一個匿名函數來封裝代碼塊,而後將其包裝在方括號中並當即執行: 函數

(function(){
    alert(2 + 2);
})();

在建立模塊化腳本時,這頗有用,以免因潛在衝突的變量而使當前範圍或全局範圍混亂(例如Greasemonkey腳本,jQuery插件等)。 lua

如今,我明白了爲何這樣作了。 方括號將內容括起來,僅顯示結果(我敢確定有一種更好的描述方式),例如(2 + 2) === 4spa


我不明白的

可是我不明白爲何這不能一樣有效: 插件

function(){
    alert(2 + 2);
}();

你能跟我解釋一下嗎? 調試


#1樓

即便這是一個古老的問答,它仍然討論了一個主題,該主題至今仍使許多開發人員陷入困境。 我沒法計算我採訪過的JavaScript開發人員候選人的人數,這些候選人沒法告訴我函數聲明和函數表達式之間的區別, 而且誰也不知道當即調用的函數表達式是什麼。 code

不過,我想提一件事,很是重要的一點是,即便Premasagar給出了名稱標識符,它的代碼段也沒法使用。 orm

function someName() {
    alert(2 + 2);
}();

之因此不起做用,是由於JavaScript引擎將其解釋爲函數聲明,而後是一個徹底不相關的不包含表達式的分組運算符,而且分組運算符必須包含一個表達式。 根據JavaScript,以上代碼片斷等同於如下代碼片斷。

function someName() {
    alert(2 + 2);
}

();

我想指出的另外一件事多是對某些人有用:您爲函數表達式提供的任何名稱標識符在代碼的上下文中幾乎沒有用,除非是在函數定義自己內部。

var a = function b() {
    // do something
};
a(); // works
b(); // doesn't work

var c = function d() {
    window.setTimeout(d, 1000); // works
};

固然,在調試代碼時,將名稱標識符與函數定義一塊兒使用老是頗有幫助的,但這徹底是另外一回事... :-)


#2樓

它不起做用是由於它被解析爲FunctionDeclaration ,而且函數聲明的名稱標識符是強制性的

當用括號將其括起來時,它將被視爲FunctionExpression ,而且能夠命名或不命名函數表達式。

FunctionDeclaration的語法以下所示:

function Identifier ( FormalParameterListopt ) { FunctionBody }

FunctionExpression s:

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

如您所見, FunctionExpressionIdentifier (Identifier opt )標記是可選的,所以咱們可使用一個未定義名稱的函數表達式:

(function () {
    alert(2 + 2);
}());

命名函數表達式:

(function foo() {
    alert(2 + 2);
}());

括號(正式稱爲分組運算符 )只能包圍表達式,而且會對函數表達式求值。

這兩個語法產生可能會模棱兩可,而且看起來可能徹底相同,例如:

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

解析器知道它是FunctionDeclaration仍是FunctionExpression ,具體取決於它出現的上下文

在上面的示例中,第二個是表達式,由於逗號運算符也只能處理表達式。

另外一方面, FunctionDeclaration實際上只能出如今所謂的「 Program 」代碼中,意味着在全局範圍以外以及在其餘函數的FunctionBody內部。

應該避免在塊內部使用函數,由於它們可能致使不可預測的行爲,例如:

if (true) { function foo() { alert('true'); } } else { function foo() { alert('false!'); } } foo(); // true? false? why?

上面的代碼實際上應該產生一個SyntaxError ,由於一個Block只能包含語句(而且ECMAScript規範沒有定義任何函數語句),可是大多數實現是能夠容忍的,而且將僅採用第二個函數,該函數會警告'false!'

Mozilla實現-Rhino,SpiderMonkey-具備不一樣的行爲。 它們的語法包含一個非標準的 Function語句,這意味着該函數將在運行時而不是在解析時進行評估,這與FunctionDeclaration s同樣。 在這些實現中,咱們將定義第一個函數。


能夠用不一樣的方式聲明函數,請比較如下內容

1-一個函數,該函數使用分配給變量的Function構造函數 乘法

var multiply = new Function("x", "y", "return x * y;");

2-名爲乘法的函數的函數聲明:

function multiply(x, y) {
    return x * y;
}

3-分配給變量multipli的函數表達式:

var multiply = function (x, y) {
    return x * y;
};

4-命名函數表達式func_name ,分配給變量multipli

var multiply = function func_name(x, y) {
    return x * y;
};

#3樓

也許簡短的答案是

function() { alert( 2 + 2 ); }

定義 (匿名)函數的函數文字 。 附加的()對,被解釋爲一個表達式,不但願在頂層使用,僅在文字上使用。

(function() { alert( 2 + 2 ); })();

調用匿名函數的表達式語句中。


#4樓

(function(){
     alert(2 + 2);
 })();

上面是有效的語法,由於括號內傳遞的任何內容都被視爲函數表達式。

function(){
    alert(2 + 2);
}();

上面的語法無效。 由於Java腳本語法分析器在function關鍵字以後查找功能名稱,由於它找不到任何東西,所以會引起錯誤。


#5樓

好的答案已經發布。 但我想指出,函數聲明返回一個空的完成記錄:

14.1.20-運行時語義:評估

FunctionDeclarationfunction BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. 返回NormalCompletion (空)。

這個事實不容易觀察,由於大多數嘗試獲取返回值的方法都會將函數聲明轉換爲函數表達式。 可是, eval顯示:

var r = eval("function f(){}"); console.log(r); // undefined

調用空的完成記錄沒有任何意義。 這就是爲何function f(){}()沒法工做。 實際上,JS引擎甚至沒有嘗試調用它,括號被視爲另外一條語句的一部分。

可是,若是將函數包裝在括號中,它將變成函數表達式:

var r = eval("(function f(){})"); console.log(r); // function f(){}

函數表達式返回一個函數對象。 所以,您能夠將其稱爲: (function f(){})()

相關文章
相關標籤/搜索