我以前知道这是什么意思,但我现在很挣扎...
这基本上是在说document.onload
吗?
(function () {
})();
这是一个立即调用的函数表达式,简称IIFE。它在创建后立即执行。
它与任何事件(例如 )的任何事件处理程序无关document.onload
。
考虑第一对括号内的部分:....它是一个正则函数表达式。然后看最后一个 pair ,这通常是添加到表达式中调用函数的;在这种情况下,我们之前的表达。(function(){})();
(function(){})();
当试图避免污染全局命名空间时,经常使用这种模式,因为在 IIFE 内部使用的所有变量(就像在任何其他普通函数中一样)在其范围之外是不可见的。
这就是为什么,也许,您将此构造与事件处理程序混淆了window.onload
,因为它经常被用作:
(function(){
// all your code here
var foo = function() {};
window.onload = foo;
// ...
})();
// foo is unreachable here (it’s undefined)
Guffa建议的更正:
该函数在创建后立即执行,而不是在解析后执行。在执行其中的任何代码之前解析整个脚本块。此外,解析代码并不自动意味着它已被执行,例如,如果 IIFE 在函数内部,那么在调用函数之前它不会被执行。
更新 由于这是一个非常流行的话题,值得一提的是,IIFE 也可以用ES6 的箭头函数编写(就像Gajus在评论中指出的那样):
((foo) => {
// do something with foo here foo
})('foo value')
它只是一个在创建后立即执行的匿名函数。
就好像您将它分配给一个变量,然后立即使用它,只是没有变量:
var f = function () {
};
f();
在 jQuery 中,您可能会想到类似的构造:
$(function(){
});
这是绑定ready
事件的简写形式:
$(document).ready(function(){
});
但是以上两个构造都不是IIFE。
立即调用函数表达式 (IIFE) 立即调用函数。这仅仅意味着函数在定义完成后立即执行。
三个更常见的措辞:
// Crockford's preference - parens on the inside
(function() {
console.log('Welcome to the Internet. Please follow me.');
}());
//The OPs example, parentheses on the outside
(function() {
console.log('Welcome to the Internet. Please follow me.');
})();
//Using the exclamation mark operator
//https://stackoverflow.com/a/5654929/1175496
!function() {
console.log('Welcome to the Internet. Please follow me.');
}();
如果对其返回值没有特殊要求,那么我们可以这样写:
!function(){}(); // => true
~function(){}(); // => -1
+function(){}(); // => NaN
-function(){}(); // => NaN
或者,它可以是:
~(function(){})();
void function(){}();
true && function(){ /* code */ }();
15.0, function(){ /* code */ }();
你甚至可以写:
new function(){ /* code */ }
31.new function(){ /* code */ }() //If no parameters, the last () is not required
该构造称为立即调用函数表达式 (IIFE),这意味着它会立即执行。将其视为解释器到达该函数时自动调用的函数。
最常见的用例:
它最常见的用例之一是限制通过var
. 通过创建的变量var
的范围仅限于函数,因此此构造(它是围绕某些代码的函数包装器)将确保您的变量范围不会从该函数中泄漏。
在下面的示例中,count
将在立即调用的函数之外不可用,即范围count
不会泄漏到函数之外。ReferenceError
如果您尝试在立即调用的函数之外访问它,您应该得到一个, 。
(function () {
var count = 10;
})();
console.log(count); // Reference Error: count is not defined
ES6 替代方案(推荐)
在 ES6 中,我们现在可以通过let
和创建变量const
。它们都是块范围的(不像var
函数范围的)。
因此,您现在可以编写更简单的代码来确保变量的范围不会泄漏到您想要的块之外,而不是为我上面提到的用例使用复杂的 IIFE 构造。
{
let count = 10;
}
console.log(count); // ReferenceError: count is not defined
在这个例子中,我们用来let
定义限制在代码块中的count
变量count
,我们用大括号创建{...}
。
我称之为“卷曲监狱”。
也就是说立即执行。
所以如果我这样做:
var val = (function(){
var a = 0; // in the scope of this function
return function(x){
a += x;
return a;
};
})();
alert(val(10)); //10
alert(val(11)); //21
小提琴:http: //jsfiddle.net/maniator/LqvpQ/
var val = (function(){
return 13 + 5;
})();
alert(val); //18
它声明了一个匿名函数,然后调用它:
(function (local_arg) {
// anonymous function
console.log(local_arg);
})(arg);
(function () {
})();
这称为 IIFE(立即调用函数表达式)。著名的 JavaScript 设计模式之一,它是现代模块模式的核心和灵魂。顾名思义,它在创建后立即执行。这种模式创建了一个孤立的或私有的执行范围。
ECMAScript 6 之前的 JavaScript 使用词法作用域,因此 IIFE 用于模拟块作用域。(在 ECMAScript 6 中,通过引入let
andconst
关键字可以实现块作用域。)
关于词法作用域问题的参考
使用 IIFE 的性能优势是能够通过减少范围查找将常用的全局对象(如window
、document
等)作为参数传递。(请记住,JavaScript 会在本地范围内查找属性,并在链中向上查找直到全局范围)。因此,在本地范围内访问全局对象可以减少查找时间,如下所示。
(function (globalObj) {
//Access the globalObj
})(window);
这是 Javascript 中立即调用的函数表达式:
要理解 JS 中的 IIFE,让我们分解一下:
a = 10 output = 10 (1+3) output = 4
// Function Expression var greet = function(name){ return 'Namaste' + ' ' + name; } greet('Santosh');
函数表达式是如何工作的:
- 当 JS 引擎第一次运行时(执行上下文 - 创建阶段),这个函数(在上面 = 的右侧)不会被执行或存储在内存中。JS 引擎为变量“greet”分配了“未定义”值。
- 在执行期间(执行上下文 - 执行阶段),函数对象是动态创建的(它尚未执行),被分配给“greet”变量,并且可以使用“greet('somename')”调用它。
3. 立即调用函数表达式:
例子:
// IIFE
var greeting = function(name) {
return 'Namaste' + ' ' + name;
}('Santosh')
console.log(greeting) // Namaste Santosh.
IIFE 的工作原理:
- 注意函数声明后的“()”。每个函数对象都有一个可调用的“CODE”属性。我们可以使用'()'大括号来调用它(或调用它)。
- 所以在这里,在执行期间(执行上下文 - 执行阶段),函数对象被创建并同时执行
- 所以现在,greeting 变量,而不是函数对象,有它的返回值(一个字符串)
IIFE 在 JS 中的典型用例:
下面的 IIFE 模式非常常用。
// IIFE
// Spelling of Function was not correct , result into error
(function (name) {
var greeting = 'Namaste';
console.log(greeting + ' ' + name);
})('Santosh');
所以这个函数被同时创建和执行(IIFE)。
IIFE 的重要用例:
IIFE 保证我们的代码安全。
- IIFE 作为一个函数,有自己的执行上下文,这意味着在其中创建的所有变量都是该函数的本地变量,不与全局执行上下文共享。
假设我的应用程序中使用了另一个 JS 文件(test1.js)以及 iife.js(见下文)。
// test1.js
var greeting = 'Hello';
// iife.js
// Spelling of Function was not correct , result into error
(function (name) {
var greeting = 'Namaste';
console.log(greeting + ' ' + name);
})('Santosh');
console.log(greeting) // No collision happens here. It prints 'Hello'.
所以 IIFE 帮助我们编写安全的代码,不会无意中与全局对象发生冲突。
不,这个结构只是创建了一个命名范围。如果你把它分成几部分,你会看到你有一个外部
(...)();
那是一个函数调用。在括号内你有:
function() {}
那是一个匿名函数。在构造内用var声明的所有内容都将仅在同一构造内可见,并且不会污染全局命名空间。
那是一个自调用匿名函数。
查看W3Schools 对自调用函数的解释。
函数表达式可以“自调用”。
自调用表达式被自动调用(启动),而不被调用。
如果表达式后跟 (),函数表达式将自动执行。
您不能自调用函数声明。
这是自调用匿名函数。它在定义时执行。这意味着此函数已定义并在定义后立即调用自身。
语法的解释是:第一个()
括号内的函数是没有名称的函数,通过下一个();
括号你可以理解它在定义时被调用。您可以在第二个括号中传递任何参数,该参数()
将在第一个括号中的函数中获取。看这个例子:
(function(obj){
// Do something with this obj
})(object);
在这里,您传递的“对象”将可以通过“obj”在函数内访问,因为您在函数签名中获取它。
从这里开始:
var b = 'bee';
console.log(b); // global
把它放在一个函数中,它就不再是全局的了——你的主要目标。
function a() {
var b = 'bee';
console.log(b);
}
a();
console.log(b); // ReferenceError: b is not defined -- *as desired*
立即调用该函数——哎呀:
function a() {
var b = 'bee';
console.log(b);
}(); // SyntaxError: Expected () to start arrow function, but got ';' instead of '=>'
使用括号来避免语法错误:
(function a() {
var b = 'bee';
console.log(b);
})(); // OK now
您可以省略函数名称:
(function () { // no name required
var b = 'bee';
console.log(b);
})();
它不需要比这更复杂。
它是一个函数表达式,它代表立即调用函数表达式(IIFE)。IIFE 只是一个在创建后立即执行的函数。因此,函数必须等到它被调用才能执行,所以 IIFE 会立即执行。让我们通过示例构建 IIFE。假设我们有一个 add 函数,它接受两个整数作为 args 并返回 sum 让 add 函数变成一个 IIFE,
第 1 步:定义函数
function add (a, b){
return a+b;
}
add(5,5);
Step2:通过将整个函数声明包装到括号中来调用函数
(function add (a, b){
return a+b;
})
//add(5,5);
第 3 步:要立即调用该函数,只需从调用中删除“添加”文本。
(function add (a, b){
return a+b;
})(5,5);
使用 IFFE的主要原因是在函数中保留私有范围。在您的 javascript 代码中,您要确保没有覆盖任何全局变量。有时您可能会意外定义一个覆盖全局变量的变量。让我们举个例子。假设我们有一个名为 iffe.html 的 html 文件,并且 body 标记内的代码是 -
<body>
<div id = 'demo'></div>
<script>
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
</body>
好吧,上面的代码将毫无问题地执行,现在假设您无意或有意地清除了一个名为 document 的变量。
<body>
<div id = 'demo'></div>
<script>
document.getElementById("demo").innerHTML = "Hello JavaScript!";
const document = "hi there";
console.log(document);
</script>
</body>
你最终会遇到一个SyntaxError : redeclaration of non-configurable global property document。
但是,如果您希望清除变量名文档,您可以使用 IFFE 来实现。
<body>
<div id = 'demo'></div>
<script>
(function(){
const document = "hi there";
this.document.getElementById("demo").innerHTML = "Hello JavaScript!";
console.log(document);
})();
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
</body>
输出:
让我们再举一个例子,假设我们有一个像下面这样的计算器对象-
<body>
<script>
var calculator = {
add:function(a,b){
return a+b;
},
mul:function(a,b){
return a*b;
}
}
console.log(calculator.add(5,10));
</script>
</body>
好吧,它就像一个魅力,如果我们不小心重新分配了计算器对象的值怎么办。
<body>
<script>
var calculator = {
add:function(a,b){
return a+b;
},
mul:function(a,b){
return a*b;
}
}
console.log(calculator.add(5,10));
calculator = "scientific calculator";
console.log(calculator.mul(5,5));
</script>
</body>
是的,您最终会遇到 TypeError:calculator.mul is not a function iffe.html
但是在 IFFE 的帮助下,我们可以创建一个私有范围,我们可以在其中创建另一个变量名计算器并使用它;
<body>
<script>
var calculator = {
add:function(a,b){
return a+b;
},
mul:function(a,b){
return a*b;
}
}
var cal = (function(){
var calculator = {
sub:function(a,b){
return a-b;
},
div:function(a,b){
return a/b;
}
}
console.log(this.calculator.mul(5,10));
console.log(calculator.sub(10,5));
return calculator;
})();
console.log(calculator.add(5,10));
console.log(cal.div(10,5));
</script>
</body>
自执行函数通常用于封装上下文并避免名称串通。您在 (function(){..})() 中定义的任何变量都不是全局变量。
编码
var same_name = 1;
var myVar = (function() {
var same_name = 2;
console.log(same_name);
})();
console.log(same_name);
产生这个输出:
2
1
通过使用这种语法,您可以避免与 JavaScript 代码中其他地方声明的全局变量发生冲突。
它被称为 IIFE - 立即调用函数表达式。这是一个显示其语法和用法的示例。它用于将变量的使用范围限定到函数之前,而不是超出函数。
(function () {
function Question(q,a,c) {
this.q = q;
this.a = a;
this.c = c;
}
Question.prototype.displayQuestion = function() {
console.log(this.q);
for (var i = 0; i < this.a.length; i++) {
console.log(i+": "+this.a[i]);
}
}
Question.prototype.checkAnswer = function(ans) {
if (ans===this.c) {
console.log("correct");
} else {
console.log("incorrect");
}
}
var q1 = new Question('Is Javascript the coolest?', ['yes', 'no'], 0);
var q2 = new Question('Is python better than Javascript?', ['yes', 'no', 'both are same'], 2);
var q3 = new Question('Is Javascript the worst?', ['yes', 'no'], 1);
var questions = [q1, q2, q3];
var n = Math.floor(Math.random() * questions.length)
var answer = parseInt(prompt(questions[n].displayQuestion()));
questions[n].checkAnswer(answer);
})();
自执行匿名函数。它在创建后立即执行。
一个有用的简短示例是:
function prepareList(el){
var list = (function(){
var l = [];
for(var i = 0; i < 9; i++){
l.push(i);
}
return l;
})();
return function (el){
for(var i = 0, l = list.length; i < l; i++){
if(list[i] == el) return list[i];
}
return null;
};
}
var search = prepareList();
search(2);
search(3);
因此,不是每次都创建一个列表,而是只创建一次(开销更少)。
function
我喜欢反例,因为它们描绘了一幅很好的逻辑图景,没有其他人列出任何东西。你可能会问,“为什么浏览器不能看到function(){}()
并假设它是一个表达式?” 让我们将这个问题与三个例子并列。
var x;
// Here, fibonacci is a block function
function fibonacci(x) {
var value = x < 2 ? x : fibonacci(x-1) + fibonacci(x-2);
if (x === 9) console.log("The " + x + "th fibonacci is: " + value);
return value;
}
(x = 9);
console.log("Value of x: " + x);
console.log("fibonacci is a(n) " + typeof fibonacci);
观察当我们把函数变成表达式时事情是如何变化的。
var x;
// Here, fibonacci is a function expression
(function fibonacci(x) {
var value = x < 2 ? x : fibonacci(x-1) + fibonacci(x-2);
if (x === 9) console.log("The " + x + "th fibonacci is: " + value);
return value;
})
(x = 9);
console.log("Value of x: " + x);
console.log("fibonacci is a(n) " + typeof fibonacci);
当您使用非运算符而不是括号时,也会发生同样的事情,因为两个运算符都将语句转换为表达式:
var x;
// Here, fibonacci is a function expression
! function fibonacci(x) {
var value = x < 2 ? x : fibonacci(x-1) + fibonacci(x-2);
if (x === 9) console.log("The " + x + "th fibonacci is: " + value);
return value;
}
(x = 9);
console.log("Value of x: " + x);
console.log("fibonacci is a(n) " + typeof fibonacci);
通过将函数转换为表达式,它可以由它(x = 9)
下面的两行执行。由于表达式函数和块函数的不同行为,两个示例都运行良好,没有歧义(规范方面)。
另一个重要的观察是命名块函数对整个范围可见,而函数表达式只对它们自己可见。换句话说,仅当它是第一个示例中的块时,fibonacci
最后才可见。console.log
在所有三个示例中,fibonacci
对自身都是可见的,允许fibonacci
调用自身,即递归。
逻辑的另一个方面是箭头函数。如果将块函数和表达式函数的定义合并在一起,则规范必须包含箭头函数的任意规则和例外:
function hello() {console.log("Hello World")}
(x) => console.log("hello " + x)
console.log("If you are reading this, no errors occurred");
尽管函数块工作正常,但函数表达式后跟箭头函数会产生语法错误:
! function hello() {console.log("Hello World")}
(x) => console.log("hello " + x)
console.log("If you are reading this, no errors occurred");
在这里,第二(x)
行是调用前一行的函数还是箭头函数的函数参数是模棱两可的。
请注意,箭头函数多年来确实符合 ECMAScript 标准,并且在语言的初始设计中并不是一个因素;我的观点是,表达式和块函数之间的区别有助于 JavaScript 语法更加合乎逻辑和连贯。
该函数称为自调用函数。自调用(也称为自执行)函数是一个无名(匿名)函数,它在定义后立即被调用(调用)。在这里阅读更多
这些函数的作用是在定义函数时立即调用该函数,这样可以节省时间和额外的代码行(与在单独的行上调用它相比)。
这是一个例子:
(function() {
var x = 5 + 4;
console.log(x);
})();
IIFE(立即调用函数表达式)是一个在脚本加载并消失后立即执行的函数。
考虑下面的函数写在一个名为 iife.js 的文件中
(function(){
console.log("Hello Stackoverflow!");
})();
上面的代码将在您加载 iife.js 后立即执行并打印“ Hello Stackoverflow!' 在开发人员工具的控制台上。
有关详细说明,请参阅立即调用函数表达式 (IIFE)
以下代码:
(function () {
})();
被称为立即调用的函数表达式(IIFE)。
之所以称为函数表达式,是因为( yourcode )
Javascript 中的运算符将其强制转换为表达式。函数表达式和函数声明之间的区别如下:
// declaration:
function declaredFunction () {}
// expressions:
// storing function into variable
const expressedFunction = function () {}
// Using () operator, which transforms the function into an expression
(function () {})
表达式只是一堆可以计算为单个值的代码。对于上面示例中的表达式,此值是单个函数对象。
在我们有一个计算结果为函数对象的表达式之后,我们可以立即使用运算符调用函数对象()
。例如:
(function() {
const foo = 10; // all variables inside here are scoped to the function block
console.log(foo);
})();
console.log(foo); // referenceError foo is scoped to the IIFE
当我们处理大型代码库和/或导入各种库时,命名冲突的可能性会增加。当我们在 IIFE 中编写相关的(因此使用相同的变量)代码的某些部分时,所有变量和函数名称的范围都在 IIFE 的函数括号内。这减少了命名冲突的机会,并且让您可以更粗心地命名它们(例如,您不必为它们加上前缀)。
另一个用例是缓存对象不是全局的记忆化:
var calculate = (function() {
var cache = {};
return function(a) {
if (cache[a]) {
return cache[a];
} else {
// Calculate heavy operation
cache[a] = heavyOperation(a);
return cache[a];
}
}
})();
这里已经有很多很好的答案,但这是我的 2 美分:p
您可以将IIFE(立即调用函数表达式)用于:
避免全局命名空间中的污染。
IIFE(甚至任何普通函数)中定义的变量不会覆盖全局范围内的定义。
保护代码不被外部代码访问。
您在 IIFE 中定义的所有内容只能在 IIFE 中访问。它保护代码不被外部代码修改。只有您作为函数的结果显式返回或设置为外部变量的值才能被外部代码访问。
避免命名不需要重复使用的函数。尽管可以在 IIFE 模式中使用命名函数,但您不必这样做,因为通常不需要重复调用它。
IIFE 通常以下列方式使用:
(function(param){
//code here
})(args);
您可以省略()
匿名函数周围的括号并在匿名函数之前使用void
运算符。
void function(param){
//code here
}(args);
立即调用的函数表达式 (IIFE) 是一个在创建后立即执行的函数。它与任何事件或异步执行无关。您可以定义一个 IIFE,如下所示:
(function() {
// all your code here
// ...
})();
第一对括号 function(){...} 将括号内的代码转换为表达式。第二对括号调用从表达式生成的函数。
AnIIFE
也可以描述为自调用匿名函数。它最常见的用法是限制通过 var 生成的变量的范围或封装上下文以避免名称冲突。
使用自我唤起匿名函数的原因是它们不应该被其他代码调用,因为它们“设置”了本应被调用的代码(以及为函数和变量提供范围)。
换句话说,它们就像在程序开始时“创建类”的程序。在它们被实例化(自动)之后,唯一可用的函数是匿名函数返回的函数。但是,所有其他 ' hidden 函数以及任何状态(在范围创建期间设置的变量)仍然存在。
很酷。
在 ES6 语法中(为我自己发帖,因为我一直在此页面上寻找一个快速示例):
// simple
const simpleNumber = (() => {
return true ? 1 : 2
})()
// with param
const isPositiveNumber = ((number) => {
return number > 0 ? true : false
})(4)
这是对为什么要使用它的更深入的解释:
“使用 IIFE 的主要原因是为了获得数据隐私。因为 JavaScript 的 var 将变量范围限定为它们的包含函数,所以在 IIFE 中声明的任何变量都不能被外界访问。”
我认为 2 组括号让它有点混乱,但我在 google 示例中看到了另一种用法,他们使用了类似的东西,我希望这能帮助你更好地理解:
var app = window.app || (window.app = {});
console.log(app);
console.log(window.app);
所以如果windows.app
没有定义,则window.app = {}
立即执行,所以在条件评估期间window.app
被赋值{}
,所以结果是两者app
,window.app
现在变成了{}
,所以控制台输出是:
Object {}
Object {}
通常,JavaScript 代码在应用程序中具有全局范围。当我们在其中声明全局变量时,就有机会在开发的其他领域将相同的重复变量用于其他目的。由于这种重复,可能会发生一些错误。所以我们可以通过立即调用函数表达式来避免这个全局变量,这个表达式是自执行表达式。当我们在这个IIFE表达式中编写代码时,全局变量就像局部范围和局部变量一样。
我们可以通过两种方式创建IIFE
(function () {
"use strict";
var app = angular.module("myModule", []);
}());
或者
(function () {
"use strict";
var app = angular.module("myModule", []);
})();
在上面的代码片段中,“<em>var app”现在是一个局部变量。
通常,我们不会在将函数写入程序后立即调用它。简而言之,当您在创建函数后立即调用它时,它被称为 IIFE - 一个花哨的名字。