Why do you need to invoke an anonymous function on the same line?
我读了一些关于闭包的文章,到处都能看到,但是没有明确的解释它是如何工作的-每次我被要求使用它时……
1 2 3 4 5 6 7 8 9 10 11 12 | // 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 })(); |
好的,我看到我们将创建新的匿名函数,然后执行它。所以在这之后,这个简单的代码就可以工作了(它确实做到了):
1 | (function (msg){alert(msg)})('SO'); |
我的问题是这里发生了什么样的魔法?我觉得当我写:
1 | (function (msg){alert(msg)}) |
然后会创建一个新的未命名函数,就像函数"(msg)……
但是为什么这不管用呢?
1 2 | (function (msg){alert(msg)}); ('SO'); |
为什么需要在同一行?
你能给我指出一些帖子或给我解释一下吗?
在函数定义后删除分号。
1 2 | (function (msg){alert(msg)}) ('SO'); |
上面应该有用。
演示页面:https://jsfiddle.net/e7ooeq6M/
我在这篇文章中讨论过这种模式:
jquery和$questions
编辑:
如果您查看ECMA脚本规范,有三种方法可以定义函数。(第98页第13节功能定义)
1。使用函数构造函数1 2 | var sum = new Function('a','b', 'return a + b;'); alert(sum(10, 20)); //alerts 30 |
2。使用函数声明。
1 2 3 4 5 6 | function sum(a, b) { return a + b; } alert(sum(10, 10)); //Alerts 20; |
三。函数表达式
1 2 3 | var sum = function(a, b) { return a + b; } alert(sum(5, 5)); // alerts 10 |
所以你可能会问,声明和表达之间有什么区别?
来自ECMA脚本规范:
FunctionDeclaration :
function Identifier ( FormalParameterListopt ){ FunctionBody
}FunctionExpression :
function Identifieropt ( FormalParameterListopt ){ FunctionBody
}
如果您注意到,"identifier"对于函数表达式是可选的。如果不提供标识符,则创建一个匿名函数。这并不意味着您不能指定标识符。
这意味着以下内容有效。
1 | var sum = function mySum(a, b) { return a + b; } |
需要注意的一点是,只能在mysum函数体内部使用mysum,而不能在外部使用。请参见以下示例:
1 2 3 4 5 | var test1 = function test2() { alert(typeof test2); } alert(typeof(test2)); //alerts 'undefined', surprise! test1(); //alerts 'function' because test2 is a function. |
现场演示
将此与
1 2 3 4 5 | function test1() { alert(typeof test1) }; alert(typeof test1); //alerts 'function' test1(); //alerts 'function' |
有了这些知识,让我们尝试分析您的代码。
当你有代码的时候,
1 | function(msg) { alert(msg); } |
您创建了一个函数表达式。您可以通过将函数表达式包装在括号中来执行它。
1 | (function(msg) { alert(msg); })('SO'); //alerts SO. |
它被称为自调用函数。
调用
有一件事让我感到困惑:"()"是分组运算符。
这是您的基本声明函数。
第1章:
1 2 3 4 5 6 7 | var message = 'SO'; function foo(msg) { alert(msg); } foo(message); |
函数是对象,可以分组。那么让我们围绕函数抛出parens。
第2章:
1 2 3 4 5 6 7 | var message = 'SO'; function foo(msg) { //declares foo alert(msg); } (foo)(message); // calls foo |
现在,我们可以使用基本替换来声明我们所调用的函数,而不是声明并立即调用相同的函数。
第3章。
1 2 3 4 5 | var message = 'SO'; (function foo(msg) { alert(msg); })(message); // declares & calls foo |
最后,我们不需要额外的foo,因为我们不使用这个名字来称呼它!函数可以是匿名的。
第4章。
1 2 3 4 5 | var message = 'SO'; (function (msg) { // remove unnecessary reference to foo alert(msg); })(message); |
要回答您的问题,请参阅示例2。第一行声明一些无名称函数并对其进行分组,但不调用它。第二行对字符串进行分组。两者都不做任何事。(文森特的第一个例子。)
1 2 3 4 5 | (function (msg){alert(msg)}); ('SO'); // nothing. (foo); (msg); //Still nothing. |
但是
1 2 | (foo) (msg); //works |
匿名函数不是名为""的函数。它只是一个没有名字的函数。
与JavaScript中的任何其他值一样,函数不需要创建名称。尽管实际上像其他值一样将它绑定到名称上更有用。
但和其他值一样,有时您希望使用它而不将其绑定到名称。这就是自调用模式。
这里有一个函数和一个数字,不受约束,它们不做任何事情,永远不能使用:
1 2 | function(){ alert("plop"); } 2; |
所以我们必须将它们存储在一个变量中才能使用它们,就像其他值一样:
1 2 | var f = function(){ alert("plop"); } var n = 2; |
还可以使用合成糖将函数绑定到变量:
1 2 | function f(){ alert("plop"); } var n = 2; |
但是,如果命名它们不是必需的,并且会导致更多的混乱和更少的可读性,那么您可以立即使用它们。
1 2 | (function(){ alert("plop"); })(); // will display"plop" alert(2 + 3); // will display 5 |
这里,我的函数和我的数字没有绑定到变量,但是它们仍然可以使用。
这样说,看起来自调用函数没有实际价值。但您必须记住,javascript范围分隔符是函数而不是块()。
因此,一个自调用函数实际上具有与C++、C语言或Java块相同的含义。这意味着内部创建的变量不会"泄漏"到范围之外。这在JavaScript中非常有用,以避免污染全局范围。
这就是JavaScript的工作原理。您可以声明一个命名函数:
1 2 3 | function foo(msg){ alert(msg); } |
并称之为:
1 | foo("Hi!"); |
或者,您可以声明一个匿名函数:
1 2 3 | var foo = function (msg) { alert(msg); } |
并称之为:
1 | foo("Hi!"); |
或者,您不能将函数绑定到名称:
1 2 3 | (function(msg){ alert(msg); })("Hi!"); |
函数还可以返回函数:
1 2 3 4 5 | function make_foo() { return function(msg){ alert(msg) }; } (make_foo())("Hi!"); |
任何在
如果您需要,这允许您封装信息:
1 2 3 4 5 6 7 | function make_greeter(msg){ return function() { alert(msg) }; } var hello = make_greeter("Hello!"); hello(); |
这只是几乎所有的编程语言,但Java工作。
你展示的代码,
1 2 | (function (msg){alert(msg)}); ('SO'); |
由两个语句组成。第一个是一个表达式,它生成一个函数对象(然后由于没有保存而被垃圾收集)。第二个表达式生成一个字符串。要将函数应用于字符串,您要么需要在创建函数时将字符串作为参数传递给函数(上面也显示了这个参数),要么需要将函数实际存储在变量中,以便您以后可以在空闲时应用它。像这样:
1 2 | var f = (function (msg){alert(msg)}); f('SO'); |
请注意,通过在变量中存储匿名函数(lambda函数),您可以有效地为其命名。因此,您也可以定义一个正则函数:
1 2 | function f(msg) {alert(msg)}; f('SO'); |
总结之前的评论:
1 2 3 | function() { alert("hello"); }(); |
当没有分配给变量时,会产生语法错误。代码被解析为一个函数语句(或定义),这使得右括号语法上不正确。在函数部分添加括号可以告诉解释器(和程序员)这是一个函数表达式(或调用),如
1 2 3 | (function() { alert("hello"); })(); |
这是一个自调用函数,意味着它是匿名创建的,并立即运行,因为调用发生在声明它的同一行中。这个自调用函数使用熟悉的语法来调用无参数函数,并在函数名周围加上括号:
有一个很好的so讨论javascript函数语法。
这个答案与这个问题没有严格的联系,但是您可能会感兴趣地发现这种语法特性并不是特定于函数的。例如,我们总是可以这样做:
1 2 3 | alert( {foo:"I am foo", bar:"I am bar"}.foo ); // alerts"I am foo" |
与功能相关。因为它们是从function.prototype继承的对象,所以我们可以执行以下操作:
1 2 3 4 5 6 7 8 9 | Function.prototype.foo = function () { return function () { alert("foo"); }; }; var bar = (function () {}).foo(); bar(); // alerts foo |
你知道,为了执行函数,我们甚至不需要用括号括起来。无论如何,只要我们尝试将结果赋给一个变量。
1 2 3 | var x = function () {} (); // this function is executed but does nothing function () {} (); // syntax error |
在声明函数后,您可以对函数做的另一件事情是对它们调用
1 2 3 4 5 6 7 | var obj = new function () { this.foo ="bar"; }; var obj = { foo :"bar" }; |
javascript函数还有一个属性。如果您想递归调用相同的匿名函数。
1 2 3 4 5 6 7 8 9 | (function forInternalOnly(){ //you can use forInternalOnly to call this anonymous function /// forInternalOnly can be used inside function only, like var result = forInternalOnly(); })(); //this will not work forInternalOnly();// no such a method exist |
我对提问者问题的理解如下:
这种魔力是如何发挥作用的:
1 | (function(){}) ('input') // Used in his example |
我可能错了。然而,人们通常熟悉的做法是:
1 | (function(){}('input') ) |
原因是,javascript括号aka
来源:blog post立即调用函数表达式(iife)
不带括号的示例:
1 2 | void function (msg) { alert(msg); } ('SO'); |
(这是虚空的唯一真正用途,阿法克)
或
1 2 | var a = function (msg) { alert(msg); } ('SO'); |
或
1 2 | !function (msg) { alert(msg); } ('SO'); |
工作也一样。
不需要换行符。
它是一个自动执行的匿名函数。第一组括号包含要执行的表达式,第二组括号执行这些表达式。
1 2 3 | (function () { return ( 10 + 20 ); })(); |
彼得·米肖在一对重要的括号中讨论了这种区别。
当试图从父命名空间隐藏变量时,它是一个有用的构造。函数中的所有代码都包含在函数的私有范围内,这意味着根本无法从函数外部访问它,从而使其真正私有。
见:
IIFE只是简单地划分函数并隐藏
1 2 3 4 | var msg ="later dude"; window.onunload = function(msg){ alert( msg ); }; |
您可以使用如下暴露的模块模式来命名您的
1 2 3 4 5 6 7 8 9 10 | var myScript = (function() { var pub = {}; //myscript.msg pub.msg ="later dude"; window.onunload = function(msg) { alert(msg); }; //API return pub; }()); |
另一种观点
首先,可以声明一个匿名函数:
1 2 3 | var foo = function(msg){ alert(msg); } |
然后你叫它:
1 | foo ('Few'); |
因为foo=function(msg)alert(msg);所以可以将foo替换为:
1 2 3 | function(msg){ alert(msg); } ('Few'); |
但是,您应该将整个匿名函数包装在一对大括号中,以避免在解析时声明函数的语法错误。然后我们有,
1 2 3 | (function(msg){ alert(msg); }) ('Few'); |
这样对我来说很容易理解。
它不能工作的简单原因不是因为
1 | function help() {return true;} |
如果调用
如果你打电话给
您所做的是通过添加分号来声明/实例化匿名函数,
1 | (function (msg) { /* Code here */ }); |
然后试图用括号在另一个语句中调用它…显然,因为函数没有名称,但这不起作用:
1 | ('SO'); |
解释器将第二行的括号视为新的指令/语句,因此它不起作用,即使您这样做:
1 | (function (msg){/*code here*/});('SO'); |
它仍然不起作用,但是当您删除分号时,它就起作用了,因为解释器忽略了空格和空格,并将完整的代码视为一条语句。
1 2 | (function (msg){/*code here*/}) // This space is ignored by the interpreter ('SO'); |
结论:一个函数调用不是一个没有
当你这样做的时候:
1 2 | (function (msg){alert(msg)}); ('SO'); |
由于分号的原因,您在
1 2 | (function (msg){alert(msg)}) ('SO'); |
它会起作用的。
工作示例:http://jsfiddle.net/oliverni/dbvjg/
1 2 | (function (msg){alert(msg)}) ('SO'); |
这是许多JavaScript框架使用匿名函数作为闭包的常见方法。
编译代码时自动调用此函数。
如果将
这也可以写成:
1 | (function (msg){alert(msg)}('SO')); |
有关更多详细信息,请查看javascript/匿名函数。
匿名函数是动态声明在运行时。它们被称为匿名函数,因为它们不是以与普通函数相同的方式给出名称。
匿名函数是使用函数运算符声明的。函数声明的。您可以使用函数运算符在表达式有效的地方创建一个新函数。为了示例可以将新函数声明为函数调用或分配另一对象的属性。
下面是一个命名函数的典型示例:
函数flyToTheMoon()alert("缩放!变焦!变焦!";}flytoThemoon();这里有一个匿名的例子功能:
var flytoThemoon=function()alert("缩放!变焦!变焦!";}FrtotoMooon();
有关详细信息,请阅读以下内容:
http://helephant.com/2008/08/23/javascript-anonymous-functions/
匿名函数应该是一次性的,您可以动态定义一个函数,以便它从您提供的输入中生成输出。但您没有提供输入。相反,您在第二行写了一些东西("so");—一个与函数无关的独立语句。你期待什么?:)