JavaScript 中的 (function() { } )() 结构是什么?
- 2024-11-02 20:59:00
- admin 原创
- 35
问题描述:
我想知道这是什么意思:
(function () {
})();
这基本上是在说吗document.onload
?
解决方案 1:
它是一个立即调用函数表达式,简称IIFE。它在创建后立即执行。
它与任何事件(例如)的任何事件处理程序都无关document.onload
。
考虑第一对括号内的部分:....它是一个正则函数表达式。然后看看最后一对,这通常添加到表达式中以调用函数;在本例中,就是我们之前的表达式。(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')
解决方案 2:
它只是一个在创建后立即执行的匿名函数。
就像你将其分配给一个变量,然后立即使用它,只是没有变量:
var f = function () {
};
f();
在 jQuery 中,有一个你可能会想到的类似结构:
$(function(){
});
这是绑定事件的简洁形式ready
:
$(document).ready(function(){
});
但是以上两个构造不是IIFE。
解决方案 3:
立即调用函数表达式 (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
解决方案 4:
该结构称为立即调用函数表达式 (IIFE),这意味着它会立即执行。可以将其视为解释器到达该函数时自动调用的函数。
最常见的用例:
其最常见的用例之一是限制通过 创建的变量的范围var
。通过 创建的变量的var
范围仅限于某个函数,因此此构造(即围绕特定代码的函数包装器)将确保您的变量范围不会泄漏到该函数之外。
在以下示例中,count
在立即调用的函数之外将不可用,即 的范围count
不会泄漏到函数之外。ReferenceError
无论如何,如果您尝试在立即调用的函数之外访问它,则应该获得 。
(function () {
var count = 10;
})();
console.log(count); // Reference Error: count is not defined
ES6 替代方案(推荐)
let
在 ES6 中,我们现在可以通过和创建变量const
。它们都是块作用域的(不同于var
函数作用域的)。
因此,对于我上面提到的用例,您现在可以编写更简单的代码来确保变量的范围不会泄漏到您想要的块之外,而不是使用 IIFE 的复杂构造。
{
let count = 10;
}
console.log(count); // ReferenceError: count is not defined
在这个例子中,我们用来let
定义限制在代码块中的count
变量count
,我们用花括号创建{...}
。
我称之为“卷曲监狱”。
解决方案 5:
也就是说立即执行。
如果我这么做:
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
解决方案 6:
它声明一个匿名函数,然后调用它:
(function (local_arg) {
// anonymous function
console.log(local_arg);
})(arg);
解决方案 7:
(function () {
})();
这称为 IIFE(立即调用函数表达式)。它是著名的 JavaScript 设计模式之一,是现代模块模式的核心。顾名思义,它在创建后立即执行。此模式创建了一个独立或私有的执行范围。
ECMAScript 6 之前的 JavaScript 使用词法作用域,因此 IIFE 用于模拟块作用域。(ECMAScript 6 引入了 andlet
关键字,块作用域成为可能const
。)
词法作用域问题参考
使用 IIFE 模拟块作用域
使用 IIFE 的性能优势在于,能够通过减少范围查找将常用的全局对象(如window
、document
等)作为参数传递。(请记住,JavaScript 会在本地范围中查找属性,并沿着链向上查找,直到全局范围)。因此,在本地范围内访问全局对象可以减少查找时间,如下所示。
(function (globalObj) {
//Access the globalObj
})(window);
解决方案 8:
这是 Javascript 中的立即调用函数表达式:
为了理解 JS 中的 IIFE,让我们将其分解一下:
表达式:返回值的
示例:在 Chrome 控制台中尝试以下操作。这些是 JS 中的表达式。
a = 10 output = 10 (1+3) output = 4
函数表达式:
示例:
// Function Expression var greet = function(name){ return 'Namaste' + ' ' + name; } greet('Santosh');
函数表达式的工作原理:
当 JS 引擎首次运行时(执行上下文 - 创建阶段),此函数(位于上方 = 的右侧)不会执行或存储在内存中。变量“greet”被 JS 引擎分配为“未定义”值。
在执行期间(执行上下文 - 执行阶段),函数对象会动态创建(尚未执行),并分配给“greet”变量,可以使用“greet('somename')”调用它。
3.立即调用函数表达式:
例子:
// IIFE
var greeting = function(name) {
return 'Namaste' + ' ' + name;
}('Santosh')
console.log(greeting) // Namaste Santosh.
IIFE 的工作原理:
注意函数声明后紧接着的 '()'。每个函数对象都附加了一个可调用的 'CODE' 属性。我们可以使用 '()' 括号来调用它(或调用它)。
因此,在执行期间(执行上下文 - 执行阶段),函数对象被创建并同时执行
因此现在,greeting 变量不再具有函数对象,而是具有其返回值(字符串)
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 帮助我们编写安全的代码,而不会无意中与全局对象发生冲突。
解决方案 9:
不,这个构造只是创建了一个命名范围。如果你把它分成几部分,你就会发现你有一个外部的
(...)();
这是一个函数调用。括号内有:
function() {}
这是一个匿名函数。在构造函数内部使用var声明的所有内容都只在同一构造函数内部可见,并且不会污染全局命名空间。
解决方案 10:
这是一个自调用的匿名函数。
查看W3Schools 对自调用函数的解释。
函数表达式可以被设计成“自调用”。
自调用表达式是自动调用(启动)的,无需被调用。
如果表达式后面有 (),函数表达式将自动执行。
您不能自行调用函数声明。
解决方案 11:
这是自调用匿名函数。它在定义时执行。这意味着此函数在定义后立即调用自身。
语法解释如下:第一个()
括号内的函数是没有名称的函数,通过下一个();
括号,您可以了解它在定义时被调用。您可以在第二个()
括号中传递任何参数,这些参数将在第一个括号内的函数中获取。请看此示例:
(function(obj){
// Do something with this obj
})(object);
这里,您传递的“对象”将可以通过“obj”在函数中访问,因为您在函数签名中抓取它。
解决方案 12:
从这里开始:
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);
})();
没必要比这更复杂。
解决方案 13:
它是一个函数表达式,代表立即调用函数表达式 (IIFE)。IIFE 只是一个在创建后立即执行的函数。因此,函数不必等到被调用才能执行,而是立即执行 IIFE。让我们通过示例构建 IIFE。假设我们有一个 add 函数,它接受两个整数作为参数并返回总和,让我们将 add 函数变成 IIFE,
步骤 1:定义函数
function add (a, b){
return a+b;
}
add(5,5);
步骤2:将整个函数声明括在括号中来调用函数
(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:重新声明不可配置的全局属性文档。
但是如果您希望清除变量名文档,则可以使用 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>
输出:
解决方案 14:
自执行函数通常用于封装上下文并避免名称串通。在 (function(){..})() 中定义的任何变量都不是全局变量。
代码
var same_name = 1;
var myVar = (function() {
var same_name = 2;
console.log(same_name);
})();
console.log(same_name);
产生以下输出:
2
1
通过使用此语法,您可以避免与在 JavaScript 代码中其他地方声明的全局变量发生冲突。
解决方案 15:
TL;DR:表达式可以用括号括起来,如果将表达式和块形式结合起来,则会与函数调用冲突。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);
运行代码片段Hide results展开片段
观察当我们将函数转换为表达式时情况如何变化。
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);
运行代码片段Hide results展开片段
当使用非运算符而不是括号时,也会发生同样的事情,因为两个运算符都会将语句转换为表达式:
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);
运行代码片段Hide results展开片段
通过将函数转换为表达式,它将由其(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");
运行代码片段Hide results展开片段
尽管函数块工作正常,但是后跟箭头函数的函数表达式会产生语法错误:
! function hello() {console.log("Hello World")}
(x) => console.log("hello " + x)
console.log("If you are reading this, no errors occurred");
运行代码片段Hide results展开片段
(x)
这里,第二行是调用上一行的函数还是箭头函数的函数参数是模棱两可的。
请注意,箭头函数多年来确实一直符合 ECMAScript 标准,并且不是该语言初始设计的一个因素;我的观点是,表达式和块函数之间的区分有助于使 JavaScript 语法更加合乎逻辑和连贯。
解决方案 16:
自执行的匿名函数。创建后立即执行。
一个简短而虚拟的例子可以说明这一点:
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);
因此,您不必每次都创建列表,而只需创建一次(更少的开销)。
解决方案 17:
它被称为 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);
})();
解决方案 18:
这里已经有很多好的答案,但这是我的一点看法 :p
您可以使用IIFE (立即调用函数表达式) 来执行以下操作:
避免全局命名空间的污染。
IIFE(或甚至任何普通函数)中定义的变量不会覆盖全局范围内的定义。
保护代码不被外部代码访问。
您在 IIFE 中定义的所有内容只能在 IIFE 中访问。它保护代码不被外部代码修改。只有您明确作为函数结果返回的内容或设置为外部变量的值的内容才可以被外部代码访问。
避免命名不需要重复使用的函数。虽然可以在 IIFE 模式中使用命名函数,但通常情况下,您不会这样做,因为不需要重复调用它。
对于许多 JS 库中使用的通用模块定义。查看此问题了解详情。
IIFE 通常以以下方式使用:
(function(param){
//code here
})(args);
()
您可以省略匿名函数周围的括号,并void
在匿名函数前使用运算符。
void function(param){
//code here
}(args);
解决方案 19:
IIFE(立即调用函数表达式)是在脚本加载并消失后立即执行的函数。
考虑在名为 iife.js 的文件中编写的以下函数
(function(){
console.log("Hello Stackoverflow!");
})();
上述代码将在您加载 iife.js 时立即执行,并在开发人员工具的控制台上打印“ Hello Stackoverflow! ”。
有关详细说明,请参阅立即调用函数表达式 (IIFE)
解决方案 20:
另一个用例是记忆化,其中缓存对象不是全局的:
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];
}
}
})();
解决方案 21:
以下代码:
(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
运行代码片段Hide results展开片段
这有什么用呢?
当我们处理大型代码库和/或导入各种库时,命名冲突的可能性会增加。当我们在 IIFE 中编写相关代码的某些部分(因此使用相同的变量)时,所有变量和函数名称的范围都限定在 IIFE 的函数括号内。这减少了命名冲突的可能性,并让您可以更随意地命名它们(例如,您不必为它们添加前缀)。
解决方案 22:
使用自调用匿名函数的原因是它们永远不应该被其他代码调用,因为它们“设置”了要调用的代码(同时为函数和变量提供了范围)。
换句话说,它们就像在程序开始时“创建类”的程序。在它们被实例化(自动)之后,唯一可用的函数是匿名函数返回的函数。但是,所有其他“隐藏”函数仍然存在,以及任何状态(范围创建期间设置的变量)。
非常酷。
解决方案 23:
使用 ES6 语法(这是我自己发的,因为我一直访问这个页面寻找一个简单的示例):
// simple
const simpleNumber = (() => {
return true ? 1 : 2
})()
// with param
const isPositiveNumber = ((number) => {
return number > 0 ? true : false
})(4)
解决方案 24:
此函数称为自调用函数。自调用(也称为自执行)函数是一种无名(匿名)函数,其定义后立即被调用。在此处阅读更多信息
这些函数的作用是,当定义函数时,立即调用该函数,从而节省时间和额外的代码行(与在单独的行上调用它相比)。
以下是一个例子:
(function() {
var x = 5 + 4;
console.log(x);
})();
运行代码片段Hide results展开片段
解决方案 25:
立即调用函数表达式 (IIFE) 是创建后立即执行的函数。它与任何事件或异步执行无关。您可以定义 IIFE,如下所示:
(function() {
// all your code here
// ...
})();
第一对括号 function(){...} 将括号内的代码转换为表达式。第二对括号调用表达式产生的函数。
也可以描述为自调用匿名函数。其IIFE
最常见的用途是限制通过 var 创建的变量的范围或封装上下文以避免名称冲突。
解决方案 26:
这是关于为什么要使用它的更深入的解释:
“使用 IIFE 的主要原因是为了获得数据隐私。由于 JavaScript 的 var 将变量范围限制在其包含函数内,因此在 IIFE 内声明的任何变量都无法被外界访问。”
解决方案 27:
通常,JavaScript 代码在应用程序中具有全局作用域。当我们在其中声明全局变量时,有可能在开发的其他区域出于其他目的使用相同的重复变量。由于这种重复,可能会发生一些错误。因此,我们可以通过使用立即调用函数表达式来避免这个全局变量,这个表达式是自执行表达式。当我们在这个IIFE表达式中编写代码时,全局变量将像局部作用域和局部变量一样。
创建IIFE 的两种方法
(function () {
"use strict";
var app = angular.module("myModule", []);
}());
或者
(function () {
"use strict";
var app = angular.module("myModule", []);
})();
在上面的代码片段中,“ var app ”现在是一个局部变量。
解决方案 28:
通常,我们不会在程序中编写函数后立即调用它。用非常简单的话来说,当你在创建函数后立即调用它时,它被称为 IIFE - 一个花哨的名字。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件