为什么你需要在同一行上调用一个匿名函数?
我正在阅读关于closures的一些post,并在任何地方看到这个post,但没有明确的解释它是如何工作的 – 每次我只是被告知使用它…:
// 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 })();
好吧,我看到我们将创build新的匿名函数,然后执行它。 所以之后,这个简单的代码应该工作(和它):
(function (msg){alert(msg)})('SO');
我的问题是这里发生了什么样的魔法? 我以为,当我写道:
(function (msg){alert(msg)})
那么就会创build一个新的未命名的函数,如函数“”(msg)…
但那为什么不工作?
(function (msg){alert(msg)}); ('SO');
为什么需要在同一行?
你能指点我一些post还是给我一个解释?
在函数定义之后放下分号。
(function (msg){alert(msg)}) ('SO');
以上应该工作。
演示页面: https : //jsfiddle.net/e7ooeq6m/
我在这篇文章中讨论过这种模式:
jQuery和$问题
编辑:
如果你看ECMA脚本规范 ,有三种方法可以定义一个函数。 (页98,部分13function定义)
1.使用Function构造函数
var sum = new Function('a','b', 'return a + b;'); alert(sum(10, 20)); //alerts 30
2.使用函数声明。
function sum(a, b) { return a + b; } alert(sum(10, 10)); //Alerts 20;
3.functionexpression
var sum = function(a, b) { return a + b; } alert(sum(5, 5)); // alerts 10
所以你可能会问,声明和expression有什么区别?
从ECMA脚本规范:
FunctionDeclaration:函数标识符(FormalParameterListopt){FunctionBody}
FunctionExpression:function Identifieropt(FormalParameterListopt){FunctionBody}
如果您注意到,“标识符”对于函数expression式是可选的。 而当你不给一个标识符,你创build一个匿名函数。 这并不意味着你不能指定一个标识符。
这意味着以下是有效的。
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); }
你创build了一个函数expression式。 你可以通过将它包装在括号中来执行这个函数expression式。
(function(msg) { alert(msg); })('SO'); //alerts SO.
这就是所谓的自我调用function。
当你调用(function(){})
时你正在做的是返回一个函数对象。 当你追加()
到它时,它被调用,并且正文中的任何东西都被执行。 The ;
表示语句的结尾,这就是第二次调用失败的原因。
有一件事我觉得困惑的是“()”是分组操作符。
这是你的基本声明函数。
防爆。 1:
var message = 'SO'; function foo(msg) { alert(msg); } foo(message);
函数是对象,可以分组。 所以让我们抛出parens的function。
防爆。 2:
var message = 'SO'; function foo(msg) { //declares foo alert(msg); } (foo)(message); // calls foo
现在,我们可以使用基本replace来声明它,而不是声明和立即调用相同的函数。
防爆。 3。
var message = 'SO'; (function foo(msg) { alert(msg); })(message); // declares & calls foo
最后,我们不需要额外的foo,因为我们不使用名称来调用它! 函数可以是匿名的。
防爆。 4。
var message = 'SO'; (function (msg) { // remove unnecessary reference to foo alert(msg); })(message);
要回答你的问题,请回头参考例2.你的第一行声明了一些无名函数并对它进行了分组,但是没有调用它。 第二行分组一个string。 两者都无所作为。 (文森特的第一个例子)
(function (msg){alert(msg)}); ('SO'); // nothing. (foo); (msg); //Still nothing.
但
(foo) (msg); //works
匿名函数不是名称为“”的函数。 这只是一个没有名字的function。
像JavaScript中的任何其他值一样,函数不需要创build名称。 尽pipe将其与任何其他值绑定到一个名称是更有用的。
但是,像其他任何价值一样,你有时想使用它而不把它绑定到一个名字上。 这是自我调动的模式。
这里是一个函数和一个数字,没有绑定,他们什么都不做,也不能使用:
function(){ alert("plop"); } 2;
所以我们必须将它们存储在一个variables中才能使用它们,就像其他值一样:
var f = function(){ alert("plop"); } var n = 2;
你也可以使用syntally sugar将函数绑定到一个variables上:
function f(){ alert("plop"); } var n = 2;
但是,如果命名它们不是必需的,会导致更多的混淆和更less的可读性,您可以立即使用它们。
(function(){ alert("plop"); })(); // will display "plop" alert(2 + 3); // will display 5
在这里,我的函数和我的数字没有绑定到一个variables,但他们仍然可以使用。
像这样说,它看起来像自我调用function没有实际价值。 但是您必须记住JavaScript范围分隔符是函数而不是块({})。
所以一个自调用函数实际上和C ++,C#或Java块有相同的含义。 这意味着内部创build的variables不会在范围之外“泄漏”。 这在JavaScript中非常有用,以免污染全球范围。
这正是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
定义的“var”variables将被make_foo
返回的每个函数closures,这是值得的。 这是一个闭包,这意味着一个函数对值的任何更改都会被另一个函数看到。
这可以让你封装信息,如果你愿意的话:
function make_greeter(msg){ return function() { alert(msg) }; } var hello = make_greeter("Hello!"); hello();
这几乎是所有的编程语言,但Java的作品。
你显示的代码,
(function (msg){alert(msg)}); ('SO');
由两个陈述组成。 第一个是产生一个函数对象的expression式(它将被垃圾回收,因为它没有被保存)。 第二个是产生一个string的expression式。 要将该函数应用于string,您需要在创build函数时将该string作为parameter passing给函数(上面还显示了这个函数),或者您需要将该函数实际存储在variables中,以便可以在以后的时间,在你的闲暇时间使用它。 像这样:
var f = (function (msg){alert(msg)}); f('SO');
请注意,通过在一个variables中存储一个匿名函数(一个lambda函数),你的名字是有效的。 因此,您可以定义一个常规函数:
function f(msg) {alert(msg)}; f('SO');
总结前面的评论:
function() { alert("hello"); }();
未分配给variables时,会产生语法错误。 该代码被parsing为一个函数语句(或定义),从而使得括号内的括号在语法上不正确。 在函数部分周围添加括号告诉解释器(和程序员),这是一个函数expression式(或调用),如
(function() { alert("hello"); })();
这是一个自我调用函数,意味着它是匿名创build的,因为调用发生在声明的同一行,所以立即运行。 这个自我调用函数用熟悉的语法来表示,以调用无参数函数,并在函数的名字周围加上括号: (myFunction)();
。
有一个很好的讨论JavaScript函数的语法 。
这个答案与问题没有严格的关系,但是你可能会感兴趣的是,这种语法特征并不是特定于函数的。 例如,我们总是可以这样做:
alert( {foo: "I am foo", bar: "I am bar"}.foo ); // alerts "I am foo"
与function有关。 因为它们是从Function.prototypeinheritance的对象,所以我们可以这样做:
Function.prototype.foo = function () { return function () { alert("foo"); }; }; var bar = (function () {}).foo(); bar(); // alerts foo
而且你知道,我们甚至不必为了执行而用括号括起函数。 无论如何,只要我们试图将结果分配给一个variables。
var x = function () {} (); // this function is executed but does nothing function () {} (); // syntax error
另一件你可以用函数做的事情,就是声明它们,就是调用它们上面的new
运算符,并获得一个对象。 以下是等同的:
var obj = new function () { this.foo = "bar"; }; var obj = { foo : "bar" };
JavaScript函数还有一个属性。 如果你想recursion调用同一个匿名函数。
(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
没有括号的例子:
void function (msg) { alert(msg); } ('SO');
(这是唯一真正的使用void,afaik)
要么
var a = function (msg) { alert(msg); } ('SO');
要么
!function (msg) { alert(msg); } ('SO');
工作也是如此。 void
导致expression评估,以及任务和爆炸。 最后一个适用于~
, +
, -
, delete
, typeof
,一些一元运算符( void
也是一个)。 不工作是因为++
,因为需要一个variables。
换行符是没有必要的。
我对提问者的问题的理解是:
这个魔法是如何工作的:
(function(){}) ('input') // Used in his example
我可能是错的。 然而,人们熟悉的惯例是:
(function(){}('input') )
原因是JavaScript括号AKA ()
不能包含语句,当分析器遇到函数关键字时,它知道把它parsing为函数expression式而不是函数声明。
来源:博客文章立即调用函数expression式(IIFE)
这是一个自我执行的匿名函数。 第一组括号包含要执行的expression式,第二组括号执行这些expression式。
(function () { return ( 10 + 20 ); })();
Peter Michaux讨论了一对重要的括号中的差异。
当试图隐藏来自父命名空间的variables时,这是一个有用的构造。 函数中的所有代码都包含在函数的私有范围内,这意味着函数的外部根本无法访问,从而使其成为私有的。
看到:
- closures(计算机科学)
- JavaScript命名空间
- 重要的一对Javascript括号
它不工作的简单理由不是因为;
指示匿名函数的结束。 这是因为没有()
函数调用的结尾,它不是一个函数调用。 那是,
function help() {return true;}
如果你调用result = help();
这是一个函数的调用,将返回true。
如果你打电话给result = help;
这不是一个电话。 这是一个分配,帮助处理就像分配给结果的数据一样。
你所做的是通过添加分号来声明/实例化一个匿名函数,
(function (msg) { /* Code here */ });
然后试着用括号括起来在另一个语句中调用它…显然,因为函数没有名字,但是这不起作用:
('SO');
口译员把第二行的括号看作是一个新的指令/陈述,因此它不起作用,即使你这样做了:
(function (msg){/*code here*/});('SO');
它仍然不起作用,但是当你删除分号时它会起作用,因为解释器会忽略空格和小车,并将完整的代码视为一个语句。
(function (msg){/*code here*/}) // This space is ignored by the interpreter ('SO');
结论:函数调用不是一个没有()
的函数调用,除非在特定条件下(例如被另一个函数调用),即,即使没有包括括号,onload ='help'也会执行帮助函数。 我相信setTimeout和setInterval也允许这种types的函数调用,而且我也相信解释器无论如何都会在后面添加括号,这使得我们回到“函数调用不是没有括号的函数调用”。
(function (msg){alert(msg)}) ('SO');
这是使用匿名函数作为许多JavaScript框架使用的闭包的常用方法。
编译代码时,调用的这个函数是自动的。
如果放置;
在第一行,编译器将其视为两条不同的行。 所以你不能得到和上面一样的结果。
这也可以写成:
(function (msg){alert(msg)}('SO'));
有关更多详细信息,请参阅JavaScript /匿名函数 。
-
匿名函数是在运行时dynamic声明的函数。 它们被称为匿名函数,因为它们没有像正常函数一样被赋予一个名字。
匿名函数是使用函数运算符而不是函数声明来声明的。 您可以使用函数运算符在需要放置expression式的任何地方创build新函数。 例如,你可以声明一个新的函数作为函数调用的参数,或者分配另一个对象的属性。
以下是一个命名函数的典型示例:
函数flyToTheMoon(){alert(“Zoom!Zoom!Zoom!”); } flyToTheMoon(); 以下是与匿名函数相同的示例:
var flyToTheMoon = function(){alert(“Zoom!Zoom!Zoom!”); } flyToTheMoon();
详情请阅读:
http://helephant.com/2008/08/23/javascript-anonymous-functions/
IIFE只是简单地划分函数,隐藏msg
variables,以免“污染”全局名称空间。 事实上,只要保持简单,就像下面这样做,除非你正在build立一个十亿美元的网站。
var msg = "later dude"; window.onunload = function(msg){ alert( msg ); };
你可以使用Revealing Module Pattern命名空间你的msg
属性,如:
var myScript = (function() { var pub = {}; //myscript.msg pub.msg = "later dude"; window.onunload = function(msg) { alert(msg); }; //API return pub; }());
匿名函数意味着一次性交易,您可以即时定义一个函数,从而根据您提供的input生成一个输出。 除了你没有提供input。 相反,你在第二行写了一些东西('SO'); – 与function无关的独立声明。 你期待什么? 🙂
另一个angular度来看
首先,你可以声明一个匿名函数:
var foo = function(msg){ alert(msg); }
那你叫它:
foo ('Few');
因为foo = function(msg){alert(msg);}所以你可以把fooreplace成:
function(msg){ alert(msg); } ('Few');
但是你应该将整个匿名函数包装在一对大括号中,以避免parsing时声明函数的语法错误。 那我们呢,
(function(msg){ alert(msg); }) ('Few');
通过这种方式,我很容易理解。
当你做了:
(function (msg){alert(msg)}); ('SO');
由于分号,您在('SO')
之前结束了函数。 如果你只写:
(function (msg){alert(msg)}) ('SO');
它会工作。
工作示例: http : //jsfiddle.net/oliverni/dbVjg/