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

IT技术 javascript anonymous-function iife
2021-01-15 12:43:38

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

// 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)})

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

但是为什么这不起作用?

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

为什么需要在同一行?

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

6个回答

删除函数定义后的分号。

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

以上应该工作。

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

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

jQuery 和 $ 问题

编辑:

如果您查看ECMA 脚本规范,可以通过 3 种方式定义函数。(第 98 页,第 13 节功能定义)

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. 函数表达式

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.
Nosredna,JS 在添加分号时表现得很少。阅读这篇详细的文章:blog.boyet.com/blog/javascriptlessons/...
2021-03-28 12:43:38
是的,但为什么呢?为什么它需要作为内联?无论我将使用多少空白。
2021-03-29 12:43:38
我认为自动分号插入会在这种情况下放入分号,但事实并非如此。所以你是对的。
2021-04-05 12:43:38
正如我所写,分号终止了匿名函数定义。因为它没有名字(它是匿名的!),你将无法再调用它。如果你不放分号,那么函数仍然可以被执行。
2021-04-06 12:43:38
是的,我看到 (function (msg){alert(msg)})('SO'); 作品。我只是问为什么它有效。这是指定的位置或这是什么类型的 JS 功能。所以一旦我调用: (function (msg){alert(msg)}) 函数会发生什么?它会被GC吗?
2021-04-06 12:43:38

它被称为自调用函数。

您在调用时所做的(function(){})是返回一个函数对象。当您附加()到它时,它会被调用并执行正文中的任何内容。;表示语句的结束,这就是为什么第二次调用失败。

就个人而言,我什至不喜欢“自调用函数”这个词。并不是函数在调用自身。程序员写了这些括号来调用它。
2021-03-13 12:43:38
不错的文章链接。它指出了为什么有人会使用引用:“为了保护全局对象,所有 JavaScript 应用程序都应该写在一个自调用函数中。这将创建一个应用程序范围,可以在其中创建变量而不必担心它们发生冲突与其他应用程序。” 还注意到“一旦函数终止,变量将被丢弃,全局对象保持不变。”
2021-03-16 12:43:38
它不是“特殊语法”,比其他任何东西都特别。实际上,“function name (args) { BLOCK }”形式更“特殊”。它实际上是不必要的糖;然而,这才是真正使事情发生的原因。
2021-03-23 12:43:38
我认为说身体将被“评估”是不正确的。它的执行就像任何其他函数一样。因为它是匿名的,您要么将引用保存在某处,要么立即执行。
2021-03-29 12:43:38
啊好吧,我明白了,所以这只是一些特殊的 JS 语法,对吗?最喜欢这个解释!简单而简短:)
2021-04-05 12:43:38

我发现令人困惑的一件事是“()”是分组运算符。

这是您的基本声明函数。

前任。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
重要的 AHA 时刻在这里 - 感谢您用替换来说明。+100
2021-03-13 12:43:38
谢谢。你的例子很清楚。我不知道 JavaScript 中的括号会以这种方式改变代码的含义。我有 Java 背景,所以几乎每天使用 JavaScript 时,我都会学到一些关于 JavaScript 的新东西(通常是出乎意料的)。
2021-03-30 12:43:38
我读过的关于匿名函数的最好的解释之一。非常感谢!
2021-04-02 12:43:38
感谢您一步一步地做到这一点,这比我见过的任何其他解释都要好得多。+1
2021-04-08 12:43:38

匿名函数不是名称为“”的函数。它只是一个没有名字的函数。

与 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

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

这么一说,貌似自调用函数没有什么实际value。但是您必须记住,JavaScript 范围分隔符是函数而不是块 ({})。

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

这个 SO 线程超出了我们在这里讨论的范围,但它解释了分离 JavaScript 命名空间的方法——并包括使用自调用函数的示例。
2021-03-16 12:43:38
function(){ alert("plop"); 指令只是分配函数但不执行它也不将它绑定到变量。由于创建的函数没有绑定任何变量,所以会很快被GCed。
2021-03-18 12:43:38
好贴。'function(){ alert("plop"); 然后会发生什么?}' 当我执行它时?它会被GC吗?
2021-04-08 12:43:38

这就是 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!");

在 的主体中用“var”定义的任何变量都make_foo将被make_foo. 这是一个闭包,它意味着一个函数对值所做的任何更改都将被另一个函数看到。

如果您愿意,这可以让您封装信息:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

除了 Java 之外,几乎所有的编程语言都是如此。