我讀了一些有關閉包的文章,處處都看到了,可是沒有明確的解釋它是如何工做的-每當我被告知要使用它時……: 編程
// Create a new anonymous function, to use as a wrapper (function(){ // The variable that would, normally, be global var msg = "Thanks for visiting!"; // Binding a new function to a global object window.onunload = function(){ // Which uses the 'hidden' variable alert( msg ); }; // Close off the anonymous function and execute it })();
好的,我看到咱們將建立一個新的匿名函數,而後執行它。 因此以後,這個簡單的代碼應該能夠工做了(而且能夠): 閉包
(function (msg){alert(msg)})('SO');
個人問題是這裏發生了什麼魔術? 我覺得當我寫: app
(function (msg){alert(msg)})
而後將建立一個新的未命名函數,如函數「」(msg)... 編程語言
可是,爲何這不起做用? ide
(function (msg){alert(msg)}); ('SO');
爲何它須要在同一行? 函數
您能給我指出一些帖子仍是給我一個解釋? spa
將分號放在函數定義以後。 .net
(function (msg){alert(msg)}) ('SO');
以上應該工做。 code
演示頁面: https : //jsfiddle.net/e7ooeq6m/ orm
我在這篇文章中討論了這種模式:
編輯:
若是您查看ECMA腳本規範 ,則能夠經過3種方式定義函數。 (第98頁,第13節「功能定義」)
var sum = new Function('a','b', 'return a + b;'); alert(sum(10, 20)); //alerts 30
function sum(a, b) { return a + b; } alert(sum(10, 10)); //Alerts 20;
var sum = function(a, b) { return a + b; } alert(sum(5, 5)); // alerts 10
因此您可能會問,聲明和表達式之間有什麼區別?
根據ECMA腳本規範:
FunctionDeclaration:函數標識符(FormalParameterListopt){FunctionBody}
FunctionExpression:function Identifieropt(FormalParameterListopt){FunctionBody}
若是您注意到,「 identifier」對於函數表達式是可選的。 當您不提供標識符時,您將建立一個匿名函數。 這並不意味着您沒法指定標識符。
這意味着跟隨是有效的。
var sum = function mySum(a, b) { return a + b; }
須要注意的重要一點是,只能在mySum函數主體內使用「 mySum」,而不能在外部使用。 請參見如下示例:
var test1 = function test2() { alert(typeof test2); } alert(typeof(test2)); //alerts 'undefined', surprise! test1(); //alerts 'function' because test2 is a function.
比較一下
function test1() { alert(typeof test1) }; alert(typeof test1); //alerts 'function' test1(); //alerts 'function'
有了這些知識,讓咱們嘗試分析您的代碼。
當您有相似的代碼時,
function(msg) { alert(msg); }
您建立了一個函數表達式。 您能夠經過將其包裝在括號內來執行此函數表達式。
(function(msg) { alert(msg); })('SO'); //alerts SO.
這稱爲自調用函數。
調用(function(){})
時,您正在作的事情是返回一個函數對象。 當您將()
附加到它時,將調用它,而且執行體內的任何內容。 的;
表示語句的結尾,這就是第二次調用失敗的緣由。
這就是JavaScript的工做方式。 您能夠聲明一個命名函數:
function foo(msg){ alert(msg); }
並稱之爲:
foo("Hi!");
或者,您能夠聲明一個匿名函數:
var foo = function (msg) { alert(msg); }
並調用:
foo("Hi!");
或者,您永遠不能將函數綁定到名稱:
(function(msg){ alert(msg); })("Hi!");
函數也能夠返回函數:
function make_foo() { return function(msg){ alert(msg) }; } (make_foo())("Hi!");
make_foo
返回的每一個函數都將關閉在make_foo
主體中用「 var」定義的任何變量,這make_foo
。 這是一個閉包,這意味着一個函數對值所作的任何更改將對另外一個函數可見。
若是須要,這使您能夠封裝信息:
function make_greeter(msg){ return function() { alert(msg) }; } var hello = make_greeter("Hello!"); hello();
這幾乎是除Java以外的每種編程語言的工做方式。
您顯示的代碼,
(function (msg){alert(msg)}); ('SO');
由兩個語句組成。 第一個是產生函數對象的表達式(因爲未保存,所以將對其進行垃圾回收)。 第二個是產生字符串的表達式。 要將函數應用於字符串,您須要在建立函數時將字符串做爲參數傳遞給函數(也在上面顯示),或者須要將函數實際存儲在變量中,以便您能夠在之後的空閒時間應用它。 像這樣:
var f = (function (msg){alert(msg)}); f('SO');
請注意,經過在變量中存儲匿名函數(lambda函數),能夠有效地爲其命名。 所以,您也能夠定義一個常規函數:
function f(msg) {alert(msg)}; f('SO');
匿名函數不是名稱爲「」的函數。 它只是一個沒有名稱的函數。
像JavaScript中的任何其餘值同樣,函數不須要建立名稱。 儘管實際上將其綁定到名稱就像其餘任何值同樣有用。
可是,像其餘任何值同樣,有時您但願使用它而不將其綁定到名稱。 這就是自我調用模式。
這是一個函數和一個數字,不受限制,它們什麼也不作,永遠不能使用:
function(){ alert("plop"); } 2;
所以,咱們必須將它們存儲在變量中才能使用它們,就像其餘任何值同樣:
var f = function(){ alert("plop"); } var n = 2;
您還可使用語法糖將函數綁定到變量:
function f(){ alert("plop"); } var n = 2;
可是,若是不須要命名它們,而且會致使更多的混亂和可讀性下降,則能夠當即使用它們。
(function(){ alert("plop"); })(); // will display "plop" alert(2 + 3); // will display 5
在這裏,個人函數和個人數字未綁定到變量,但仍可使用。
這樣說,看起來自調用功能沒有實際價值。 可是您必須記住,JavaScript做用域分隔符是函數而不是塊({})。
所以,自調用函數實際上與C ++,C#或Java塊具備相同的含義。 這意味着在內部建立的變量不會在範圍外「泄漏」。 這在JavaScript中很是有用,以避免污染全局範圍。