我读了一些有关闭包的文章,处处都看到了,可是没有明确的解释它是如何工做的-每当我被告知要使用它时……: 编程
// 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中很是有用,以避免污染全局范围。