12

你把这些模式叫做什么?它们之间有什么区别?你什么时候使用每个?还有其他类似的模式吗?

(function() {
    console.log(this);  // window
})();

(function x() {
    console.log(this);  // window
})();

var y = (function() {
    console.log(this);  // window
})();

var z = function() {
    console.log(this);  // window
}();

编辑:通过命名最后两种情况下的函数,我发现了另外两种看似多余的方法......

var a = (function foo() {
    console.log(this);  // window
})();

var b = function bar() {
    console.log(this);
}();

EDIT2: 这是@GraceShao 下面提供的另一种模式,它使函数可以在函数范围之外访问。

(x = function () {
    console.log(this);  // window
    console.log(x);     // function x() {}
})();
console.log(x);         // function x() {}

// I played with this as well 
// by naming the inside function 
// and got the following:

(foo = function bar() {
    console.log(this);  // window
    console.log(foo);   // function bar() {}
    console.log(bar);   // function bar() {}
})();
console.log(foo);       // function bar() {}
console.log(bar);       // undefined
4

6 回答 6

36

以下是您的函数,并附有一些注释,描述了它们何时/为什么有用:

(function() {
    // Create a new scope to avoid exposing 
    // variables that don't need to be
    // This function is executed once immediately
})();

(function fact(i) {
    // This named immediately invoked function 
    // is a nice way to start off recursion
    return i <= 1 ? 1 : i*fact(i - 1);
})(10);

var y = (function() {
    // Same as the first one, but the return value 
    // of this function is assigned to y
    return "y's value";
})();

var z = function() {
    /* This is the exact same thing as above 
     (except it is assigned to z instead of y, of course).
     The parenthesis in the above example don't do anything
     since this is already an expression
    */
}();
于 2012-06-11T17:23:08.773 回答
6

在这种情况下,它们在语义上都是相同的。ECMAScript 规范包含完整的生产规则,所以这是一个大体上的简化。

另请注意,我忽略了命名函数的名称 ( x),因为未使用该名称;它可以在正文中引用,但由于它是一个FunctionExpression(通过语法产生),它永远不会(在正确的JS 实现中)污染包含范围 - 请参阅注释。

(function() {
    console.log(this);  // window
})();

(function x() {
    console.log(this);  // window
})();

var y = (function() {
    console.log(this);  // window
})();

var z = function() {
    console.log(this);  // window
}();

减少(在这种情况下,主体是无关紧要的,它们都“返回未定义”):

(function() {})();

(function x() {})();

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

var z = function() {}();

简化(在 ECMAScript 语法FunctionExpression中是生产规则,但在这里我用它来表示“作为函数的表达式”):

FunctionExpression()

FunctionExpression()

var y = FunctionExpression()

var z = FunctionExpression()

忽略结果的赋值(总是undefined)可以看出所有的形式都是一样的。

快乐编码。

于 2012-06-11T17:31:59.317 回答
2

自调用匿名函数。函数体将立即被调用。

(function() {
    console.log(this);  // window
})();

自调用函数。函数体将立即被调用。您仍然可以在x函数体内引用该函数。所以当你想立即执行某事,然后你可能想迭代它,你可以直接引用它。

(function x() {
    console.log(this);  // window
    console.log(x);     // function x() {}
})();

右侧的自调用匿名函数会立即被调用,并将返回值赋给y. 通常当你使用这个模式时它有一个返回值,否则,y 将是undefined.

var y = (function() {
    console.log(this);  // window
})();

IMO,它与第三个相同。封闭函数的第三个括号只是为了使函数看起来像一个完整的东西。但是两者的功能是一样的。

var z = function() {
    console.log(this);  // window
}();

与第二个类似,但您可以使用以下方法在函数范围之外引用 x:

(x = function () {
    console.log(this);  // window
    console.log(x);     // function x() {}
})();
console.log(x);         // function x() {}
于 2012-06-11T17:34:32.113 回答
0

(function() { 'use strict'; 可以使用这种类型

为什么?:IIFE 立即调用函数表达式从全局范围中删除变量。这有助于防止变量和函数声明在全局范围内的生存时间超过预期,这也有助于避免变量冲突。

为什么?:当您的代码被压缩并捆绑到一个文件中以部署到生产服务器时,您可能会遇到变量和许多全局变量的冲突。IIFE 通过为每个文件提供可变范围来保护您免受这两种情况的影响。

于 2014-11-21T04:07:28.583 回答
0

是时候使用 ES06 了,这里是使用 ES06 中箭头函数的函数。

 (() => {
    // Create a new scope to avoid exposing variables that don't need to be
    // This function is executed once immediately
})();

(fact = (i)=>(
  // This named immediately invoked function is a nice way to start off recursion
  i <= 1 ? 1 : i*fact(i - 1)
))(10)

const y = (() => (
    // Same as the first one, but the return value of this function is assigned to y
     "y's value"
))();

const z = (() => {
    // This is the exact same thing as above (except it's assigned to z instead of y, of course).
    // The parenthesis in the above example don't do anything since this is already an expression
})();
于 2018-05-23T12:33:48.530 回答
0

浏览器中的 JavaScript 缺少命名空间。每一段代码都在全局范围内运行;因此,内部应用程序代码或第三方依赖项可能会在暴露自己的功能时污染范围。污染全局命名空间会导致名称冲突。这种名称冲突在大型项目中非常常见,并且可能非常有害。

例如,想象一下第三方库实例化了一个名为 utils 的全局变量。如果任何其他库或应用程序代码本身意外覆盖或更改了 utils,则依赖它的代码可能会以某种不可预知的方式崩溃。如果其他库或应用程序代码意外调用另一个仅供内部使用的库的函数,也可能会发生不可预测的副作用。

 (function () {
      // create state variables
      // make some operation
      // then return those
      const export = {
           export1: () => {},
           export2: () => {}
      }
      return exported
})()

它用于创建私有范围,仅导出应公开的部分。

函数表达式周围的括号

为什么我们甚至需要这些?原因纯粹是语法上的。JavaScript 解析器必须能够轻松地区分函数声明和函数表达式。如果我们省略函数表达式周围的括号,并将我们的直接调用作为单独的语句function(){}(3),JavaScript 解析器将开始处理它,并得出结论,因为它是一个以关键字 function 开头的单独语句,它正在处理一个函数宣言。因为每个函数声明都必须有一个名称(这里我们没有指定一个),所以会抛出一个错误。为了避免这种情况,我们将函数表达式放在括号中,向 JavaScript 解析器发出信号,它正在处理一个表达式,而不是一个语句。

您可能还会在某些项目中看到这一点:

+function(){}();
-function(){}();
!function(){}();
~function(){}();

这一次,我们可以使用一元运算符,而不是在函数表达式周围使用括号来将它们与函数声明区分开来+ , - , ! , and ~:我们这样做是为了向 JavaScript 引擎发出信号,表明它正在处理表达式而不是语句。

于 2022-01-24T21:37:26.897 回答