繁体   English   中英

JavaScript 中的 (function() { } )() 结构是什么?

[英]What is the (function() { } )() construct in JavaScript?

我想知道这是什么意思:

(function () {

})();

这基本上是在说document.onload吗?

It’s an Immediately-Invoked Function Expression, or IIFE for short. It executes immediately after it’s created.

It has nothing to do with any event-handler for any events (such as document.onload).
Consider the part within the first pair of parentheses: (function(){})();....it is a regular function expression. Then look at the last pair (function(){})();, this is normally added to an expression to call a function; in this case, our prior expression.

This pattern is often used when trying to avoid polluting the global namespace, because all the variables used inside the IIFE (like in any other normal function) are not visible outside its scope.
This is why, maybe, you confused this construction with an event-handler for window.onload, because it’s often used as this:

(function(){
  // all your code here
  var foo = function() {};
  window.onload = foo;
  // ...
})();
// foo is unreachable here (it’s undefined)

Correction suggested by Guffa:

The function is executed right after it's created, not after it is parsed. The entire script block is parsed before any code in it is executed. Also, parsing code doesn't automatically mean that it's executed, if for example the IIFE is inside a function then it won't be executed until the function is called.

Update Since this is a pretty popular topic, it's worth mentioning that IIFE's can also be written with ES6's arrow function (like Gajus has pointed out in a comment) :

((foo) => {
 // do something with foo here foo
})('foo value')

它只是一个匿名的 function,它在创建后立即执行。

就好像您将它分配给一个变量,然后立即使用它,只是没有变量:

 var f = function () { }; f();

在 jQuery 中,您可能会想到类似的构造:

 $(function(){ });

这是绑定ready事件的简写形式:

 $(document).ready(function(){ });

但是以上两个构造都不是IIFE

立即调用的 function 表达式 (IIFE) 立即调用 function。 这仅仅意味着 function 在定义完成后立即执行。

三个更常见的措辞:

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

该构造称为立即调用 Function 表达式 (IIFE) ,这意味着它会立即执行。 将其视为 function 在解释器到达 function 时自动调用。

最常见的用例:

它最常见的用例之一是限制通过var生成的变量的 scope 。 Variables created via var have a scope limited to a function so this construct (which is a function wrapper around certain code) will make sure that your variable scope doesn't leak out of that function.

在以下示例中, count在立即调用的 function 之外不可用,即count的 scope 不会从 function 泄漏。 如果您尝试在立即调用的 function 之外访问它,您应该得到一个ReferenceError

 (function () { var count = 10; })(); console.log(count); // Reference Error: count is not defined

ES6 替代方案(推荐)

在 ES6 中,我们现在可以通过letconst创建变量。 它们都是块范围的(与函数范围的var不同)。

因此,您现在可以编写更简单的代码来确保变量的 scope 不会从您想要的块中泄漏出来,而不是使用 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,然后调用它:

 (function (local_arg) { // anonymous function console.log(local_arg); })(arg);
 (function () { })();

这称为 IIFE(立即调用 Function 表达式)。 著名的 JavaScript 设计模式之一,它是现代模块模式的核心和灵魂。 顾名思义,它在创建后立即执行。 此模式创建一个隔离的或私有的 scope 执行。

ECMAScript 6 之前的 JavaScript 使用词法作用域,因此 IIFE 用于模拟块作用域。 (通过引入letconst关键字,ECMAScript 6 块作用域是可能的。) 关于词法作用域问题的参考

使用 IIFE 模拟块作用域

使用 IIFE 的性能优势是能够通过减少 scope 查找将常用的全局对象(如windowdocument等)作为参数传递。 (请记住 JavaScript 在本地 scope 中查找属性,然后沿着链向上直到全局范围)。 因此访问本地 scope 中的全局对象可以减少查找时间,如下所示。

 (function (globalObj) { //Access the globalObj })(window);

这是在 Javascript 中立即调用的 Function 表达式:

要理解 JS 中的 IIFE,让我们分解一下:

  1. 表达式:返回值的东西
    示例:在 chrome 控制台中尝试以下操作。 这些是 JS 中的表达式。
 a = 10 output = 10 (1+3) output = 4
  1. Function 表达式
    例子:
 // Function Expression var greet = function(name){ return 'Namaste' + ' ' + name; } greet('Santosh');

function 表达式如何工作:
- 当 JS 引擎第一次运行时(执行上下文 - 创建阶段),这个 function(在上面 = 的右侧)不会被执行或存储在 memory 中。 JS 引擎为变量“greet”分配了“未定义”值。
- 在执行期间(执行上下文 - 执行阶段),函数 object 是动态创建的(尚未执行),分配给“greet”变量,可以使用“greet('somename')'调用它。

3. 立即调用函数表达式:

例子:

 // IIFE var greeting = function(name) { return 'Namaste' + ' ' + name; }('Santosh') console.log(greeting) // Namaste Santosh.

IIFE 的工作原理
- 注意紧跟在 function 声明之后的“()”。 每个函数 object 都有一个可调用的“CODE”属性。 我们可以使用'()'大括号来调用它(或调用它)。
- So here, during the execution (Execution Context - Execute Phase), the function object is created and its executed at the same time - So now, the greeting variable, instead of having the funtion object, has its return value ( a string )

IIFE 在 JS 中的典型用例:

下面的 IIFE 模式非常常用。

 // IIFE // Spelling of Function was not correct, result into error (function (name) { var greeting = 'Namaste'; console.log(greeting + ' ' + name); })('Santosh');
  • 我们在这里做两件事。 a) 将我们的 function 表达式包裹在大括号 () 中。 这将告诉语法解析器,放在 () 中的任何内容都是表达式(在这种情况下为函数表达式)并且是有效代码。
    b) 我们同时使用函数末尾的 () 来调用这个函数。

所以这个 function 被同时创建和执行(IIFE)。

IIFE 的重要用例:

IIFE 保证我们的代码安全。
- IIFE,作为 function,有自己的执行上下文,这意味着在其中创建的所有变量都是此 function 的本地变量,并且不与全局执行上下文共享。

假设我的应用程序中使用了另一个 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 帮助我们编写安全的代码,不会无意中与全局对象发生冲突。

不,这个构造只是创建了一个 scope 用于命名。 如果你把它分成几部分,你会看到你有一个外部

(...)();

那是一个 function 调用。 在括号内你有:

 function() {}

那是一个匿名的 function。 在构造内用var声明的所有内容都将仅在同一构造内可见,并且不会污染全局命名空间。

这是自调用匿名 function。 它在定义时执行。 这意味着此 function 已定义并在定义后立即调用自身。

语法的解释是:第一个()括号内的 function 是没有名称的 function 和下一个(); 括号可以理解为在定义的时候调用。 您可以在第二个()括号中传递任何参数,该参数将在第一个括号中的 function 中获取。 看这个例子:

 (function(obj){ // Do something with this obj })(object);

在这里,您传递的“对象”将可以通过“obj”在 function 中访问,因为您在 function 签名中抓取它。

那是一个自调用匿名 function

查看W3Schools 对自调用 function 的解释

Function 表达式可以“自调用”。

自调用表达式被自动调用(启动),而不被调用。

Function 表达式如果后跟 () 将自动执行。

您不能自行调用 function 声明。

从这里开始:

 var b = 'bee'; console.log(b); // global

把它放在 function 中,它不再是全球性的——你的主要目标。

 function a() { var b = 'bee'; console.log(b); } a(); console.log(b); // ReferenceError: b is not defined -- *as desired*

立即致电 function -- 哎呀:

 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 名称:

 (function () { // no name required var b = 'bee'; console.log(b); })();

它不需要比这更复杂。

它是一个 function 表达式,它代表立即调用的 Function 表达式 (IIFE)。 IIFE 只是一个 function,它在创建后立即执行。 因此,由于 function 必须等到它被调用才能执行,所以 IIFE 会立即执行。 让我们通过示例构建 IIFE。 假设我们有一个 add function ,它接受两个整数作为参数并返回总和,让我们将 function 添加到 IIFE 中,

步骤 1:定义 function

 function add (a, b){ return a+b; } add(5,5);

Step2: 调用 function 通过将整个函数声明包装到括号中

(function add (a, b){ return a+b; }) //add(5,5);

第 3 步:要立即调用 function,只需从调用中删除“添加”文本。

 (function add (a, b){ return a+b; })(5,5);

使用 IFFE 的主要原因是在 function 中保留私有 scope。 在 javascript 代码中,您要确保没有覆盖任何全局变量。 有时您可能会意外定义一个覆盖全局变量的变量。 让我们举个例子。 假设我们有一个名为 iffe.html 的 html 文件,并且正文标签内的代码是-

 <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>

Output:

在此处输入图像描述

让我们再举一个例子,假设我们有一个计算器 object,如下所示——

 <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>

好吧,它就像一个魅力,如果我们不小心重新分配了计算器 object 的值怎么办。

 <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 的帮助下,我们可以创建一个私有的 scope,我们可以在其中创建另一个变量名计算器并使用它;

 <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 ab; }, 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>

Output: 在此处输入图像描述

自执行函数通常用于封装上下文并避免名称串通。 您在 (function(){..})() 中定义的任何变量都不是全局变量。

编码

var same_name = 1; var myVar = (function() { var same_name = 2; console.log(same_name); })(); console.log(same_name);

产生这个 output:

 2 1

通过使用这种语法,您可以避免与 JavaScript 代码中其他地方声明的全局变量发生冲突。

它被称为 IIFE - 立即调用 Function 表达式。 这是一个显示其语法和用法的示例。 它用于 scope 变量的使用仅到 function 并且不超过。

 (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); })();

TL;DR:表达式可以用括号括起来,如果将 function 的表达式和块function组合在一起,则会与 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);

观察当我们将 function 转换为表达式时情况如何变化。

 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)

通过将 function 转换为表达式,它由(x = 9)下两行执行。 由于表达式函数和块函数的不同行为,两个示例都运行良好,没有歧义(规范方面)。

名称范围

另一个重要的观察是命名块函数对整个 scope 可见,而 function 表达式只对它们自己可见。 换句话说, 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 块工作正常,但 function 表达式后跟箭头 function 会产生语法错误:

 . function hello() {console.log("Hello World")} (x) => console.log("hello " + x) console,log("If you are reading this; no errors occurred")

Here, it is ambiguous whether the (x) on line two is calling the function on the preceding line or whether it is the function arguments for an arrow function.

请注意,箭头函数多年来确实符合 ECMAScript 标准,并且在语言的初始设计中并不是一个因素; 我的观点是,表达式和块函数之间的区别有助于 JavaScript 语法更加合乎逻辑和连贯。

自执行匿名 function。 它在创建后立即执行。

一个有用的简短示例是:

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

因此,不是每次都创建一个列表,而是只创建一次(开销更少)。

另一个用例是缓存 object 不是全局的记忆化:

 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]; } } })();

IIFE(立即调用 function 表达式)是一个 function,它在脚本加载并消失后立即执行。

考虑下面写在名为 iife.js 的文件中的 function

 (function(){ console.log("Hello Stackoverflow;"); })()

上面的代码将在您加载 iife.js 后立即执行,并将在开发人员工具的控制台上打印“ Hello Stackoverflow ”。

有关详细说明,请参阅立即调用 Function 表达式 (IIFE)

以下代码:

 (function () { })();

被称为立即调用的 function 表达式(IIFE)。

它被称为 function 表达式,因为( yourcode )运算符将其强制转换为表达式。 function 表达式function 声明之间的区别如下:

 // declaration: function declaredFunction () {} // expressions: // storing function into variable const expressedFunction = function () {} // Using () operator, which transforms the function into an expression (function () {})

表达式只是一堆可以计算为单个值的代码。 对于上述示例中的表达式,此值为单个 function object

在我们有一个计算结果为 function object 的表达式之后,我们可以立即使用 6 ()运算符调用function ZA8CFDE6666。 例如:

 (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 中编写与(因此使用相同变量)相关的代码的某些部分时,所有变量和 function 名称的范围都在 IIFE 的 function 括号内 这减少了命名冲突的机会,并且让您可以更粗心地命名它们(例如,您不必为它们加上前缀)。

这个 function 称为自调用 function。 自调用(也称为自执行)function 是一个无名(匿名)function,在其定义后立即调用(调用)。 在这里阅读更多

这些函数的作用是,当定义 function 时,立即调用 function,这节省了时间和额外的代码行(与在单独的行上调用它相比)。

这是一个例子:

 (function() { var x = 5 + 4; console.log(x); })();

这里已经有很多好的答案,但这是我的 2 美分:p


您可以将IIFE (立即调用 Function 表达式)用于:

  1. 避免全局命名空间中的污染。

    IIFE(甚至任何普通函数)中定义的变量不会覆盖全局 scope 中的定义。

  2. 保护代码不被外部代码访问。

    您在 IIFE 中定义的所有内容只能在 IIFE 中访问。 它保护代码不被外部代码修改。 只有您作为 function 的结果显式返回或设置为外部变量的值才能被外部代码访问。

  3. 避免命名不需要重复使用的函数。 虽然可以在 IIFE 模式中使用名为 function 的名称,但您不必这样做,因为通常不需要重复调​​用它。

  4. 对于许多 JS 库中使用的通用模块定义 检查此问题以获取详细信息。


IIFE 通常以下列方式使用:

 (function(param){ //code here })(args);

您可以省略匿名 function 周围的括号() ,并在匿名 function 之前使用void运算符。

 void function(param){ //code here }(args);

立即调用的 function 表达式 (IIFE) 是一个 function,它在创建后立即执行。 它与任何事件或异步执行无关。 您可以定义一个 IIFE,如下所示:

 (function() { // all your code here //... })();

第一对括号 function(){...} 将括号内的代码转换为表达式。第二对括号调用由表达式生成的 function。

IIFE也可以描述为自调用匿名 function。 它最常见的用法是限制通过 var 生成的变量的 scope 或封装上下文以避免名称冲突。

使用自诱发匿名函数的原因是它们不应该被其他代码调用,因为它们“设置”了本应被调用的代码(以及将 scope 赋予函数和变量)。

换句话说,它们就像在程序开始时“创建类”的程序。在它们被实例化(自动)之后,唯一可用的函数是匿名 function 返回的函数。但是,所有其他' hidden' 功能仍然存在,以及任何 state(在 scope 创建期间设置的变量)。

很酷。

在 ES6 语法中(为我自己发帖,因为我一直在此页面上寻找一个快速示例):

 // simple const simpleNumber = (() => { return true? 1: 2 })() // with param const isPositiveNumber = ((number) => { return number > 0? true: false })(4)

这是对为什么要使用它的更深入的解释:

“使用 IIFE 的主要原因是为了获得数据隐私。因为 JavaScript 的 var 范围变量包含 function,所以在 IIFE 中声明的任何变量都不能被外界访问。”

http://adripofjavascript.com/blog/drips/an-introduction-to-iffes-immediately-invoked-function-expressions.html

JavaScript 函数语法

function name(parameter1, parameter2, parameter3) {
  // code to be executed
}

函数要执行的代码放在大括号内:{}

演示:-

let x = myFunction(4, 3);   // Function is called, return value will end up in x

function myFunction(a, b) {
  return a * b;             // Function returns the product of a and b
}
console.log(x);

x 的输出:-

12

没有功能的相同演示:-

let a= 4;
let b=3;
let x = a + b;
console.log(x);

我认为 2 组括号让它有点混乱,但我在 google 示例中看到了另一种用法,他们使用了类似的东西,我希望这能帮助你更好地理解:

 var app = window.app || (window.app = {}); console.log(app); console.log(window.app);

so if windows.app is not defined, then window.app = {} is immediately executed, so window.app is assigned with {} during the condition evaluation, so the result is both app and window.app now become {} , so控制台 output 是:

 Object {} Object {}

通常,JavaScript 代码在应用程序中具有全局 scope。 当我们在其中声明全局变量时,就有机会在开发的其他领域将相同的重复变量用于其他目的。 由于这种重复,可能会发生一些错误。 所以我们可以通过立即调用 function 表达式来避免这个全局变量,这个表达式是自执行表达式。当我们在这个IIFE表达式中编写代码时,全局变量将类似于局部 scope 和局部变量。

我们可以通过两种方式创建IIFE

 (function () { "use strict"; var app = angular.module("myModule", []); }());

或者

(function () { "use strict"; var app = angular.module("myModule", []); })();

在上面的代码片段中,“ var app ”现在是一个局部变量。

通常,我们不会在将 function 写入程序后立即调用它。 简而言之,当您在创建 function 后立即调用它时,它被称为 IIFE - 一个花哨的名字。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM