JavaScript中的(函数() { } )() 构造是什么?

我想知道这是什么意思:

(function () {
})();

这基本上是说document.onload吗?

378061 次浏览

它是一个立即调用函数表达式,简称II FE。它在创建后立即执行。

它与任何事件(例如document.onload)的任何事件处理程序无关。
考虑第一对括号内的部分:(function(){})();……它是一个正则函数表达式。然后看看最后一对(function(){})();,这通常添加到表达式中以调用函数;在这种情况下,我们的先前表达式。

当试图避免污染全局命名空间时,通常会使用此模式,因为IIFE中使用的所有变量(与任何其他正常函数一样)在其范围之外都不可见。
这就是为什么,也许,您将此构造与window.onload的事件处理程序混淆,因为它通常用作:

(function(){// all your code herevar 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 val = (function(){var a = 0;  // in the scope of this functionreturn function(x){a += x;return a;};})();
alert(val(10)); //10alert(val(11)); //21

小提琴:http://jsfiddle.net/maniator/LqvpQ/


第二个例子:

var val = (function(){return 13 + 5;})();
alert(val); //18

它声明了一个匿名函数,然后调用它:

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

这是一个自调用匿名函数

看看W3学校对自调用函数的解释

函数表达式可以“自调用”。

自调用表达式自动调用(启动),无需被召唤

函数表达式将自动执行,如果表达式是接下来是()。

您不能自调用函数声明。

不,这个构造只是为命名创建了一个作用域。如果你把它分成几个部分,你可以看到你有一个外部的

(...)();

这是一个函数调用。在括号内你有:

function() {}

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

自动执行的匿名函数。它一创建就被执行。

一个有用的简短和虚拟的例子是:

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

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

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

这就像你把它赋值给一个变量,然后马上使用它,只是没有变量:

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

在jQuery中,你可能会想到一个类似的结构:

$(function(){});

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

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

但是上面的两个构造不是IIFE

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

该代码

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

生成此输出:

21

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

立即调用的函数表达式(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

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

语法的解释是:第一个()括号中的函数是没有名称的函数,通过下一个();括号,你可以理解它在定义时被调用。你可以传递第二个()括号中的任何参数,这些参数将在第一个括号中的函数中被抓取。看这个例子:

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

在这里,您传递的“对象”将通过“obj”在函数内访问,因为您在函数签名中获取它。

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

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

因此,如果没有定义windows.app,则立即执行window.app = {},因此在条件评估期间window.app被分配了{},因此结果是appwindow.app现在都变成了{},因此控制台输出为:

Object {}Object {}
(function () {})();

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

ECMAScript 6之前的JavaScript使用词法作用域,因此IIFE用于模拟块作用域。(在ECMAScript 6中,通过引入letconst关键字可以进行块作用域。)词汇范围问题参考

使用IIFE模拟块范围

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

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

另一个用例是缓存对象不是全局的记忆:

var calculate = (function() {var cache = {};return function(a) {
if (cache[a]) {return cache[a];} else {// Calculate heavy operationcache[a] = heavyOperation(a);return cache[a];}}})();

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

最常见用例:

它最常见的用例之一是限制通过var创建的变量的范围。通过var创建的变量的范围仅限于函数,因此此构造(它是围绕某些代码的函数包装器)将确保您的变量范围不会从该函数中泄漏。

在下面的示例中,count在立即调用的函数之外不可用,即count的范围不会从函数中泄漏。如果您尝试在立即调用的函数之外访问它,您应该得到ReferenceError

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

ES6替代品(推荐)

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

因此,您现在可以编写更简单的代码来确保变量的范围不会从您想要的块中泄漏出来,而不是将IIFE的复杂构造用于我上面提到的用例。

{let count = 10;}console.log(count);  // ReferenceError: count is not defined

在这个例子中,我们使用let来定义count变量,这使得count仅限于我们使用花括号{...}创建的代码块。

我称之为“卷曲监狱”。

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

考虑下面的函数写在一个名为iife.js的文件中

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

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

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

从这里开始:

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 requiredvar b = 'bee';console.log(b);})();

没必要比这更复杂了。

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

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

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

IIFE也可以被描述为自调用匿名函数。它最常见的用法是限制通过var创建的变量的范围或封装上下文以避免名称冲突。

使用自我唤起的匿名函数的原因是它们永远不应该被其他代码调用,因为它们“设置”了IS要调用的代码(以及为函数和变量提供范围)。

换句话说,它们就像程序开始时“制造类”的程序。它们被实例化后(自动),唯一可用的函数是匿名函数返回的函数。然而,所有其他“隐藏”函数仍然存在,以及任何状态(在范围创建期间设置的变量)。

非常酷。

通常情况下,JavaScript代码在应用程序中具有全局作用域。当我们在其中声明全局变量时,有可能在开发的其他区域出于其他目的使用相同的重复变量。因为这种重复可能会发生一些错误。所以我们可以通过使用立即调用函数表达式来避免这个全局变量,这个表达式是自执行表达式。

有两种方法可以创建IIFE

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

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

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

这是JavaScript中立即调用的函数表达式:

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

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

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

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

示例:

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

生活如何运作
-注意函数声明之后的'()'。每个funtion对象都有一个可调用的'CODE'属性。我们可以使用'()'大括号调用它(或调用它)。
-所以在这里,在执行期间(执行上下文-执行阶段),函数对象被创建并同时执行-所以现在,问候变量,而不是函数对象,有它的返回值(字符串)

JS中IIFE的典型用例:

以下IIFE模式非常常用。

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

所以这个函数同时被创建和执行(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 () {
})();

被称为立即调用函数表达式(IIFE)。

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

// declaration:function declaredFunction () {}
// expressions:
// storing function into variableconst 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 blockconsole.log(foo);
})();
console.log(foo);  // referenceError foo is scoped to the IIFE

为什么这个有用?

当我们处理大型代码库和/或导入各种库时,命名冲突的可能性会增加。当我们在IIFE中编写与所有变量和函数名的作用域为IIFE的函数括号。相关(因此使用相同的变量)的代码的某些部分时。这减少了命名冲突的机会,让你可以更粗心地命名它们(例如,你不必为它们添加前缀)。

它被称为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);})();

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

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

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

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

下面是一个例子:

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

这是一个更深入的解释,为什么你会使用这个:

“使用IIFE的主要原因是获得数据隐私。因为JavaScript的var作用域将变量作用于它们的包含函数,所以在IIFE中声明的任何变量都不能被外部世界访问。”

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

这是一个函数表达式,它代表立即调用函数表达式(IIFE)。IIFE只是一个在创建后立即执行的函数。因此,如果函数必须等到被调用才能执行,IIFE会立即执行。让我们通过示例构造IIFE。假设我们有一个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步:要立即调用函数,只需从调用中删除“add”文本。

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

好吧,上面的代码将在没有任何问题的情况下执行,现在假设您意外地或故意地删除了一个名为documents的变量。

<body><div id = 'demo'></div><script>document.getElementById("demo").innerHTML = "Hello JavaScript!";const document = "hi there";console.log(document);</script></body>

您将在语法错误:重新声明不可配置的全局属性文档中结束。

但是,如果您的愿望是声明变量名文档,您可以使用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不是一个函数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>

输出:输入图片描述

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


您可以使用IIFE(立即调用函数表达式):

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

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

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

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

  3. 避免命名不需要重复使用的函数。虽然可以在IIFE模式中使用命名函数,但通常不需要重复调用它。

  4. 对于许多JS库中使用的通用模块定义。查看此问题了解详细信息。


IIFE通常以以下方式使用:

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

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

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

太长别读:表达式可以包含在括号中,与函数调用如果冲突function的表达式和块形式被合并。

我喜欢反例,因为它们描绘了一幅很好的逻辑图景,而没有人列出任何反例。你可能会问,“为什么浏览器看不到function(){}()并假设它是一个表达式?”让我们将这个问题与三个例子并列。

var x;
// Here, fibonacci is a block functionfunction 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");

在这里,第2行的(x)是在调用前一行的函数,还是它是箭头函数的函数参数,这是不明确的。

请注意,箭头函数多年来一直是ECMAScript标准,并不是语言最初设计的一个因素;我的观点是,表达式和块函数之间的区别有助于JavaScript语法更具逻辑性和连贯性。

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