为什么需要在同一行上调用匿名函数?

我读了一些关于闭包的帖子,到处都看到了这个,但是没有明确的解释它是如何工作的——每次我都被告知要使用它…:

// 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');

我的问题是这里发生了什么魔法?当我写到:

(function (msg){alert(msg)})

然后将创建一个新的未命名函数,如function ""(msg)…

但为什么这行不通呢?

(function (msg){alert(msg)});
('SO');

为什么要在同一条线上?

你能给我指出一些帖子或者给我一个解释吗?

301145 次浏览

在函数定义之后删除分号。

(function (msg){alert(msg)})
('SO');

以上应该可以工作。

演示页面:https://jsfiddle.net/e7ooeq6m/

我在这篇文章中讨论了这种模式:

jQuery和$ questions

编辑:

如果你看ECMA脚本规范,有3种方法可以定义一个函数。(第98页,第13节函数定义)

1. 使用函数构造函数

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Using Function声明。

function sum(a, b)
{
return a + b;
}


alert(sum(10, 10)); //Alerts 20;

3.函数表达式

var sum = function(a, b) { return a + b; }


alert(sum(5, 5)); // alerts 10

你可能会问,声明和表达式有什么区别?

来自ECMA脚本规范:

< p > FunctionDeclaration: 函数标识符(FormalParameterListopt){函数体 } < / p > < p > FunctionExpression: function Identifieropt (FormalParameterListopt){FunctionBody } < / p >

如果你注意到,'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.

Live Demo .

将其与

 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(){})时所做的是返回一个函数对象。当你将()附加到它之后,它就会被调用,body中的任何内容都会被执行。;表示语句的结束,这就是第二次调用失败的原因。

这就是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"定义的变量都将被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;

你也可以使用synatic sugar将函数绑定到一个变量:

function f(){ alert("plop"); }
var n = 2;

但是如果不需要命名它们,并且会导致更多的混乱和更低的可读性,那么您可以立即使用它们。

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

在这里,我的函数和数字没有绑定到一个变量,但它们仍然可以使用。

这样说,看起来自调用函数没有真正的价值。但您必须记住,JavaScript范围分隔符是函数而不是块({})。

因此,自调用函数实际上与c++、c#或Java块具有相同的含义。这意味着在内部创建的变量不会“泄漏”到作用域之外。为了不污染全局作用域,这在JavaScript中非常有用。

这个答案与问题没有严格的联系,但是您可能有兴趣发现这种语法特性并不是函数特有的。例如,我们总是可以这样做:

alert(
{foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

与函数相关。因为它们是继承自Function的对象。原型,我们可以这样做:

Function.prototype.foo = function () {
return function () {
alert("foo");
};
};


var bar = (function () {}).foo();


bar(); // alerts foo

你知道,我们甚至不需要用圆括号来包围函数来执行它们。不管怎样,只要我们试着把结果赋值给一个变量。

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"
};

我感到困惑的一件事是“()”是分组操作符。

这是你声明的基本函数。

例1:

var message = 'SO';


function foo(msg) {
alert(msg);
}


foo(message);

函数是对象,可以分组。我们在函数周围加上括号。

例2:

var message = 'SO';


function foo(msg) {  //declares foo
alert(msg);
}


(foo)(message);     // calls foo

现在,我们不用声明并立即调用相同的函数,而是可以在调用时使用基本替换来声明它。

例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。第一行声明了一些无名函数并对其进行分组,但不调用它。第二行对字符串进行分组。两者都不做任何事。(文森特的第一个例子。)

(function (msg){alert(msg)});
('SO');                       // nothing.


(foo);
(msg); //Still nothing.

(foo)
(msg); //works

JavaScript函数还有一个属性。如果你想递归调用同一个匿名函数。

(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

综上所述:

function() {
alert("hello");
}();

当未赋值给变量时,将产生语法错误。代码被解析为函数语句(或定义),这使得闭括号在语法上不正确。在函数部分周围添加圆括号告诉解释器(和程序员)这是一个函数表达式(或调用),如

(function() {
alert("hello");
})();

这是一个自调用函数,这意味着它是匿名创建的并立即运行,因为调用发生在声明它的同一行中。这个自调用函数用熟悉的语法表示为调用无参数函数,在函数名周围加上括号:(myFunction)();

一个很好的SO讨论JavaScript函数语法

匿名函数意味着一次性处理,即动态地定义一个函数,以便它根据您提供的输入生成一个输出。除了你没有提供输入。相反,你在第二行写了一些东西(‘SO’);-与函数无关的独立语句。你还指望什么?:)

我对提问者问题的理解是:

这个魔法是如何起作用的:

(function(){}) ('input')   // Used in his example

我可能错了。然而,人们熟悉的通常做法是:

(function(){}('input') )

原因是JavaScript括号(又名())不能包含语句,当解析器遇到function关键字时,它知道将其解析为函数表达式而不是函数声明。

来源:博客文章立即调用函数表达式(IIFE) . rref ="http://benalman.com/news/2010/11/immediately-invoked-function-expression/" rel="nofollow

另一个观点

首先,你可以声明一个匿名函数:

var foo = function(msg){
alert(msg);
}

然后你称之为:

foo ('Few');

因为Foo =函数(msg){警报(msg);},所以你可以将喷火替换为:

function(msg){
alert(msg);
} ('Few');

但是您应该将整个匿名函数包装在一对大括号中,以避免在解析时声明函数时出现语法错误。然后我们有,

(function(msg){
alert(msg);
}) ('Few');

这样,我就很容易理解了。

它是一个自动执行的匿名函数。第一组括号包含要执行的表达式,第二组括号执行这些表达式。

(function () {
return ( 10 + 20 );
})();

Peter Michaux讨论了一对重要的括号中的区别。

当试图从父名称空间隐藏变量时,它是一个有用的构造。函数中的所有代码都包含在函数的私有作用域中,这意味着它根本不能从函数外部访问,这使得它真正是私有的。

看到的:

  1. 闭合(计算机科学) .
  2. < a href = " http://peter.michaux。ca/articles/ JavaScript - namespaces " rel="nofollow">JavaScript命名空间 . xml
  3. < a href = " http://peter.michaux。ca/articles/an- Important - Pair -parens" rel="nofollow">重要的Javascript括号 . ca/articles/an- Important - Pair -parens" rel="nofollow

它不能工作的简单原因不是因为;指示匿名函数的结束。这是因为在函数调用的末尾没有(),它就不是函数调用。也就是说,

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也允许这种类型的函数调用,我也相信解释器在幕后添加了括号,这让我们回到了“没有括号的函数调用就不是函数调用”。

当你这样做的时候:

(function (msg){alert(msg)});
('SO');

由于分号的存在,函数在('SO')之前结束。如果你这样写:

(function (msg){alert(msg)})
('SO');

它会起作用的。

工作示例:http://jsfiddle.net/oliverni/dbVjg/

(function (msg){alert(msg)})
('SO');

这是一种使用匿名函数作为闭包的常见方法,许多JavaScript框架都使用这种方法。

编译代码时自动调用此函数。

如果将;放在第一行,编译器会将其视为两行。所以你不能得到和上面一样的结果。

这也可以写成:

(function (msg){alert(msg)}('SO'));

要了解更多细节,请查看JavaScript/匿名函数

不带括号的例子:

void function (msg) { alert(msg); }
('SO');

(这是void的唯一真正用途,afaik)

var a = function (msg) { alert(msg); }
('SO');

!function (msg) { alert(msg); }
('SO');

同时也要工作。void导致表达式求值,以及赋值和bang。最后一个操作符适用于~+-deletetypeof,以及一些一元操作符(void也是一个)。不工作的当然是++--,因为变量的要求。

换行符是不必要的。

匿名函数是在 运行时。它们被称为匿名函数,因为它们不是 以与普通函数相同的方式给出一个名称

匿名函数使用函数操作符声明 函数声明的。可以使用函数操作符to 在任何可以放置表达式的地方创建一个新函数。为 例如,您可以将一个新函数声明为参数 函数调用或赋值另一个对象的属性

下面是一个命名函数的典型例子:

function 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变量,以便不“污染”全局命名空间。事实上,只要保持简单,就像下面这样做,除非你正在建立一个十亿美元的网站。

var msg = "later dude";
window.onunload = function(msg){
alert( msg );
};

你可以像这样使用显示模块模式命名你的msg属性:

var myScript = (function() {
var pub = {};
//myscript.msg
pub.msg = "later dude";
window.onunload = function(msg) {
alert(msg);
};
//API
return pub;
}());