【问题标题】:Javascript immediately invoked function patternsJavascript 立即调用函数模式
【发布时间】:2012-06-14 15:02:13
【问题描述】:

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

(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

【问题讨论】:

  • 它的“模式”被称为“自调用函数”或“自评估函数”。在这种情况下,它们在语义上都是相同的。但是,它们关联的“名称”和结果(如果有)的规则略有不同。通常,它们对于创建新的词法范围很有用。
  • 在所有情况下,您都在创建一个函数并调用它。在某些情况下,您会将返回值分配给变量,而在其他情况下则不会。
  • 我个人更喜欢 Ben Alman 的说法:Immediately-Invoked Function Expression (IIFE)
  • 这里要注意的关键是函数不会自行执行。正如 Esailija 所说,递归是自执行函数的一个例子。在示例中,有些东西正在调用函数,但它不是函数本身。

标签: javascript function design-patterns


【解决方案1】:

以下是您的函数,其中一些 cmets 描述了它们何时/为何有用:

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

【讨论】:

  • +1 可能值得注意的是,您通常也会存储第二个等函数的结果。
  • 除了我提到的方法之外,您还有其他方法可以做到这一点吗?
  • 你还应该提到第三个和第四个例子的范围问题:)
  • @alnafie 函数是表达式而不是语句的任何地方,都可以使用() 立即执行。所以!function(){console.log('Hi');}()+function(){console.log('Hi');}() 都会执行。
  • @FlorianMargaine 您能否详细说明范围界定问题? :)
【解决方案2】:

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

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

(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)可以看出所有的形式都是一样的。

编码愉快。

【讨论】:

  • 看它的有趣方式!
  • 请注意,IE(9 之前)是不正确的 JS 实现示例,其中命名函数表达式确实污染了包含范围。
  • @MatthewCrumley 所以如果我理解正确的话,如果你使用第二个版本,IE 会创建 window.x 吗?
  • @alnafie 是的,您可以轻松测试它:(function x() { console.log(this); // window })(); window.x; //Undefined in IE &gt;8 or real browser
  • @alnafie 在IE &lt; 8 中,您可以将其包装在另一个闭包中(您问题中的第一个函数):(function(){ (function x() { })(); })(); window.x // undefined in all browsers
【解决方案3】:

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

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

【讨论】:

  • 你不能在x之外引用x
  • @PaulP.R.O.我还添加了可以在作用域外引用x的场景。
  • 是的,这行得通,但请记住,该代码使 x 成为全局变量,除非它在与 var x; 相同的范围内声明之前
【解决方案4】:

(函数() { 'use strict';你可以使用这种类型

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

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

【讨论】:

    【解决方案5】:

    是时候使用 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
    })();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 2017-08-30
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 2012-09-02
      • 2014-08-10
      相关资源
      最近更新 更多