我正在阅读一些关于闭包的帖子,并且到处都看到了这个,但是没有明确的解释它是如何工作的——每次我被告知要使用它......:
// 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');
为什么它需要在同一行?
你能指点我一些帖子或给我一个解释吗?
(function (msg){alert(msg)})('SO');
行完全独立运行。它与您之前发布的其他匿名函数无关。这是两个完全独立的匿名函数。您必须立即调用一个匿名函数,因为它没有名称并且以后不能被引用。
删除函数定义后的分号。
(function (msg){alert(msg)})
('SO');
以上应该工作。
演示页面:https://jsfiddle.net/e7ooeq6m/
我在这篇文章中讨论了这种模式:
编辑:
如果您查看 ECMA script specification,您可以通过 3 种方式定义函数。 (第 98 页,第 13 节功能定义)
1.使用函数构造函数
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:函数Identifieropt(FormalParameterListopt){FunctionBody}
如果您注意到,“标识符”对于函数表达式是可选的。当您不提供标识符时,您将创建一个匿名函数。这并不意味着您不能指定标识符。
这意味着以下是有效的。
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(){})
时所做的是返回一个函数对象。当您将 ()
附加到它时,它会被调用并执行正文中的任何内容。 ;
表示语句的结束,这就是第二次调用失败的原因。
我发现令人困惑的一件事是“()”是分组运算符。
这是您的基本声明函数。
前任。 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(){ 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 中非常有用。
这就是 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');
总结之前的评论:
function() {
alert("hello");
}();
当未分配给变量时,会产生语法错误。代码被解析为函数语句(或定义),这使得右括号在语法上不正确。在函数部分周围添加括号告诉解释器(和程序员)这是一个函数表达式(或调用),如
(function() {
alert("hello");
})();
这是一个自调用函数,这意味着它是匿名创建并立即运行的,因为调用发生在声明它的同一行。这个自调用函数以调用无参数函数的熟悉语法表示,并在函数名称周围添加括号:(myFunction)();
。
有a good SO discussion JavaScript function syntax。
我对提问者问题的理解是:
这个魔法是如何工作的:
(function(){}) ('input') // Used in his example
我可能错了。但是,人们熟悉的通常做法是:
(function(){}('input') )
原因是 JavaScript 括号 AKA ()
不能包含语句,并且当解析器遇到 function 关键字时,它知道将其解析为函数表达式而不是函数声明。
来源:博文Immediately-Invoked Function Expression (IIFE)
不带括号的例子:
void function (msg) { alert(msg); }
('SO');
(这是void的唯一真正用途,afaik)
或者
var a = function (msg) { alert(msg); }
('SO');
或者
!function (msg) { alert(msg); }
('SO');
也可以工作。 void
导致表达式进行评估,以及赋值和爆炸。最后一个适用于 ~
、+
、-
、delete
、typeof
和一些一元运算符(void
也是一个)。由于需要变量,因此无法正常工作 ++
、--
。
换行不是必需的。
delete
上的@Bergi 有效。即使使用 'use strict';
。这也有效:delete (3 + 4);
这个答案与问题并不严格相关,但您可能有兴趣发现这种语法特征并不是函数特有的。例如,我们总是可以这样做:
alert(
{foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"
与功能有关。由于它们是继承自 Function.prototype 的对象,因此我们可以执行以下操作:
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"
};
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 () {
return ( 10 + 20 );
})();
Peter Michaux 讨论了 An Important Pair of Parentheses 中的差异。
当试图从父命名空间隐藏变量时,这是一个有用的结构。函数内的所有代码都包含在函数的私有范围内,这意味着它根本无法从函数外部访问,使其真正成为私有的。
看:
闭包(计算机科学) JavaScript 命名空间 重要的 Javascript 括号对
另一个角度来看
首先,您可以声明一个匿名函数:
var foo = function(msg){
alert(msg);
}
然后你叫它:
foo ('Few');
因为 foo = function(msg){alert(msg);} 所以你可以将 foo 替换为:
function(msg){
alert(msg);
} ('Few');
但是您应该将整个匿名函数包装在一对大括号内,以避免在解析时声明函数的语法错误。然后我们有,
(function(msg){
alert(msg);
}) ('Few');
这样,对我来说很容易理解。
当你这样做时:
(function (msg){alert(msg)});
('SO');
由于分号,您在 ('SO')
之前结束了函数。如果你只写:
(function (msg){alert(msg)})
('SO');
它会起作用的。
工作示例:http://jsfiddle.net/oliverni/dbVjg/
它不起作用的简单原因不是因为 ;
指示匿名函数的结束。这是因为函数调用末尾没有 ()
,它就不是函数调用。那是,
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');
这是使用匿名函数作为许多 JavaScript 框架使用的闭包的常用方法。
编译代码时会自动调用此函数。
如果将 ;
放在第一行,编译器会将其视为两个不同的行。所以你不能得到与上面相同的结果。
这也可以写成:
(function (msg){alert(msg)}('SO'));
有关详细信息,请查看 JavaScript/Anonymous Functions。
IIFE 只是简单地划分函数并隐藏 msg
变量,以免“污染”全局命名空间。实际上,除非您要建立一个十亿美元的网站,否则请保持简单并像下面那样做。
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;
}());
匿名函数是在运行时动态声明的函数。它们被称为匿名函数,因为它们的名称与普通函数不同。
匿名函数是使用函数运算符而不是函数声明来声明的。您可以使用函数运算符在任何可以放置表达式的地方创建新函数。例如,您可以将新函数声明为函数调用的参数或分配另一个对象的属性。
这是命名函数的典型示例:
function flyToTheMoon() {
alert("Zoom! Zoom! Zoom!");
}
flyToTheMoon();
这是作为匿名函数创建的相同示例:
var flyToTheMoon = function() {
alert("Zoom! Zoom! Zoom!");
}
flyToTheMoon();
详情请阅读http://helephant.com/2008/08/23/javascript-anonymous-functions/
匿名函数意味着一次性交易,您可以在其中动态定义函数,以便它从您提供的输入中生成输出。除非您没有提供输入。相反,你在第二行写了一些东西('SO'); - 与功能无关的独立声明。你期待什么? :)
var foo = function() {};
。其他一切都很好。
不定期副业成功案例分享