【问题标题】:What is the purpose of a self executing function in javascript?javascript中自执行函数的目的是什么?
【发布时间】:2010-10-10 04:40:27
【问题描述】:

在 javascript 中,你想什么时候使用这个:

(function(){
    //Bunch of code...
})();

在此:

//Bunch of code...

【问题讨论】:

标签: javascript closures iife self-executing-function


【解决方案1】:

简单。看起来很正常,几乎令人欣慰:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

但是,如果我在我的页面中包含一个非常方便的 javascript 库,可以将高级字符转换为其基本级别的表示,该怎么办?

等等……什么?

我的意思是,如果有人键入带有某种重音的字符,但我的程序中只需要“英文”字符 A-Z?嗯……西班牙语的“ñ”和法语的“é”字符可以翻译成“n”和“e”的基本字符。

因此,某个好人编写了一个全面的字符转换器,我可以将其包含在我的网站中...我将其包含在内。

一个问题:它里面有一个函数叫做'name',和我的函数一样。

这就是所谓的碰撞。我们在同一个作用域中声明了两个同名的函数。我们希望避免这种情况。

所以我们需要以某种方式限定我们的代码范围。

在 javascript 中限定代码范围的唯一方法是将其包装在一个函数中:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter library's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

这可能会解决我们的问题。现在所有内容都被封闭了,只能从我们的左大括号和右大括号内访问。

我们有一个函数中的一个函数......看起来很奇怪,但完全合法。

只有一个问题。我们的代码不起作用。 我们的userName 变量永远不会回显到控制台!

我们可以通过在现有代码块之后添加对我们函数的调用来解决这个问题...

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

或者之前!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

次要问题:“main”这个名字还没有被使用的可能性有多大? ...非常非常苗条。

我们需要更多的范围界定。还有一些方法可以自动执行我们的 main() 函数。

现在我们来谈谈自动执行功能(或自执行、自运行等)。

((){})();

语法很笨拙。但是,它有效。

当您将函数定义包装在括号中并包含参数列表(另一个集合或括号!)时,它充当函数调用

让我们再看看我们的代码,使用一些自动执行的语法:

(function main() {
  var userName = "Sean";
                
    console.log(name());
                
    function name() {
      return userName;
    }
  }
)();

因此,在您阅读的大多数教程中,您现在会被“匿名自执行”或类似术语轰炸。

经过多年的专业发展,我强烈敦促您为您编写的每个函数命名以进行调试。

当出现问题时(并且会出现问题),您将在浏览器中检查回溯。当堆栈跟踪中的条目有名称时,总是更容易缩小代码问题的范围!

非常啰嗦,希望对您有所帮助!

【讨论】:

  • 谢谢您:) 我一直在搜索互联网,试图了解 IIFE 在可变隐私方面相对于正常功能的优势,而您的答案简直是最好的。每个人都说,最大的优势之一是 IIFE 中的变量和函数“最终”是私有的,而普通函数只是为您提供完全相同的东西。最后我想我通过你的解释过程明白了。 IIFE毕竟只是一个功能,但现在我明白为什么要使用它了。再次感谢您!
  • 感谢您抽出宝贵时间详细解释这一点。
  • 不错的答案。不过,我对您的最后一点有一个问题-当您建议命名所有函数时,您是说有一种方法可以使用自执行函数来做到这一点,还是建议每个人都命名该函数然后调用它?编辑哦,我明白了。这个已经命名了。呃。可能想指出您正在证明使用命名的自执行函数是合理的。
  • 好吧,我的朋友,这就是我一直在寻找的答案:)
  • 我一直喜欢两种答案; (1.) 短小精悍,切中要害。 (2.) 一个像解释一样的故事,永远存在于你的大脑中。你的属于 (2.)
【解决方案2】:

您可以使用此函数返回值:

var Test = (function (){
        
        
        const alternative = function(){ return 'Error Get Function '},
        methods = {
            GetName: alternative,
            GetAge:alternative
            }
            
            

// If the condition is not met, the default text will be returned
// replace to  55 < 44
if( 55 > 44){



// Function one
methods.GetName = function (name) {
        
        return name;

};

// Function Two

methods.GetAge = function (age) {
        
        return age;

};





}










    return methods;
    
    
    }());
    
    
    
    
    
    
    
    // Call
   console.log( Test.GetName("Yehia") );

    console.log( Test.GetAge(66) );

【讨论】:

    【解决方案3】:

    自执行函数用于管理变量的范围。

    变量的范围是定义它的程序区域。

    全局变量具有全局范围;它在您的 JavaScript 代码中随处定义,并且可以从脚本中的任何位置访问,甚至在您的函数中。另一方面,在函数中声明的变量仅在函数体内定义。 它们是局部变量,具有局部范围,并且只能在该函数内访问。函数参数也算作局部变量,只在函数体内定义。

    如下所示,您可以在函数内部访问全局变量变量,同时注意在函数体中,局部变量优先于同名的全局变量。

    var globalvar = "globalvar"; // this var can be accessed anywhere within the script
    
    function scope() {
        alert(globalvar);
        var localvar = "localvar"; //can only be accessed within the function scope
    }
    
    scope(); 
    

    所以基本上一个自执行函数允许编写代码而不用关心变量在其他 javascript 代码块中是如何命名的。

    【讨论】:

      【解决方案4】:

      一个区别是您在函数中声明的变量是本地的,因此当您退出函数时它们会消失,并且不会与其他或相同代码中的其他变量冲突。

      【讨论】:

        【解决方案5】:

        这都是关于变量范围的。默认情况下,自执行函数中声明的变量仅可用于自执行函数中的代码。这允许编写代码而不用关心变量在其他 JavaScript 代码块中是如何命名的。

        例如,Alexander 的评论中提到:

        (function() {
          var foo = 3;
          console.log(foo);
        })();
        
        console.log(foo);

        这将首先记录3,然后在下一个console.log 上抛出错误,因为foo 未定义。

        【讨论】:

        • 也为了许多人的利益,包括一大群 Netflix 工程师:这只是一个功能。它本身并不代表关闭。有时自动调用器与闭包相关的场景一起使用来做一些简洁的事情,但是如果你没有看到一些东西持有一个将被垃圾收集并在非闭包语言中消失的引用,那么它就没有什么可做的了用 CLOSURES 做这件事。
        • 所以这意味着它主要与闭包一起使用?
        • @AlexanderBird 但这已经发生在函数内部的局部变量中:function(){ var foo = 3; alert(foo); }; alert(foo); 所以我还是不明白
        • 如果只是为了作用域,为什么不直接使用{ let foo = 3 }
        • @Giulio 这个答案来自 2009 年。块范围是后来才引入的
        【解决方案6】:

        鉴于您的简单问题:“在 javascript 中,您什么时候想使用这个:...”

        我喜欢 @ken_browning 和 @sean_holding 的回答,但这里有另一个我没有提到的用例:

        let red_tree = new Node(10);
        
        (async function () {
            for (let i = 0; i < 1000; i++) {
                await red_tree.insert(i);
            }
        })();
        
        console.log('----->red_tree.printInOrder():', red_tree.printInOrder());
        

        其中 Node.insert 是一些异步操作。

        我不能在我的函数声明中只调用 await 没有 async 关键字,我不需要命名函数供以后使用,但需要等待该插入调用或者我需要一些其他更丰富的功能(谁知道吗?)。

        【讨论】:

          【解决方案7】:

          首先你必须访问MDN IIFE,现在关于这个的一些要点

          • 这是立即调用函数表达式。因此,当您的 javascript 文件从 HTML 调用时,此函数会立即调用。
          • 这可以防止访问 IIFE 惯用语中的变量以及污染全局范围。

          【讨论】:

            【解决方案8】:

            简短的回答是:防止污染全球(或更高)范围。

            IIFE(立即调用函数表达式)是将脚本编写为插件、加载项、用户脚本或预期可与其他人的脚本一起使用的任何脚本的最佳实践。这可确保您定义的任何变量都不会对其他脚本产生不良影响。

            这是编写 IIFE 表达式的另一种方式。我个人更喜欢以下这种方法:

            void function() {
              console.log('boo!');
              // expected output: "boo!"
            }();
            

            https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

            从上面的例子很明显,IIFE 也会影响效率和性能,因为预计只运行一次的函数将执行一次,然后永远转储到 void 中 .这意味着函数或方法声明不会保留在内存中。

            【讨论】:

            • 很好,我之前没见过void 的用法。我喜欢它。
            【解决方案9】:
            (function(){
                var foo = {
                    name: 'bob'
                };
                console.log(foo.name); // bob
            })();
            console.log(foo.name); // Reference error
            

            其实上面的函数会被当成没有名字的函数表达式。

            用右括号和左括号包装函数的主要目的是避免污染全局空间。

            函数表达式内的变量和函数变为私有(即)它们在函数外不可用。

            【讨论】:

              【解决方案10】:

              我已阅读所有答案,这里缺少一些非常重要的内容,我会亲吻。有两个主要原因,为什么我需要自执行匿名函数,或者说“立即调用函数表达式 (IIFE)”:

              1. 更好的命名空间管理(避免命名空间污染 -> JS 模块)
              2. 闭包(模拟私有类成员,从 OOP 中得知)

              第一个解释得很好。对于第二个,请研究以下示例:

              var MyClosureObject = (function (){
                var MyName = 'Michael Jackson RIP';
                return {
                  getMyName: function () { return MyName;},
                  setMyName: function (name) { MyName = name}
                }
              }());
              

              注意 1:我们没有将函数分配给 MyClosureObject,更进一步调用该函数的结果。请注意最后一行中的()

              注意2:关于Javascript中的函数你还需要了解的是,内部函数可以访问函数的参数和变量,它们是定义的内。

              让我们尝试一些实验:

              我可以使用getMyName 获得MyName,它可以工作:

               console.log(MyClosureObject.getMyName()); 
               // Michael Jackson RIP
              

              以下巧妙的方法行不通:

              console.log(MyClosureObject.MyName); 
              // undefined
              

              但我可以设置另一个名称并获得预期的结果:

              MyClosureObject.setMyName('George Michael RIP');
              console.log(MyClosureObject.getMyName()); 
              // George Michael RIP
              

              编辑:在上面的示例中,MyClosureObject 设计为在没有 new前缀的情况下使用,因此按照惯例,它不应大写。

              【讨论】:

              • 你的回答是我第一次意识到可以(function(){ }( ))而不是问题的语法(function(){ })();他们似乎达到了相同的结果。
              【解决方案11】:

              看起来这个问题已经准备就绪,但我还是会发布我的意见。

              我知道我什么时候喜欢使用自执行函数。

              var myObject = {
                  childObject: new function(){
                      // bunch of code
                  },
                  objVar1: <value>,
                  objVar2: <value>
              }
              

              该函数允许我使用一些额外的代码来定义 childObjects 的属性和属性以使代码更简洁,例如设置常用变量或执行数学方程;哦!或错误检查。而不是局限于...的嵌套对象实例化语法

              object: {
                  childObject: {
                      childObject: {<value>, <value>, <value>}
                  }, 
                  objVar1: <value>,
                  objVar2: <value>
              }
              

              一般来说,编码有很多晦涩难懂的方式来做很多相同的事情,这让你想知道,“为什么要麻烦?”但是,新情况不断出现,您不能再单独依赖基本/核心原则。

              【讨论】:

                【解决方案12】:

                这是一个自调用匿名函数如何有用的可靠示例。

                for( var i = 0; i < 10; i++ ) {
                  setTimeout(function(){
                    console.log(i)
                  })
                }
                

                输出:10, 10, 10, 10, 10...

                for( var i = 0; i < 10; i++ ) {
                  (function(num){
                    setTimeout(function(){
                      console.log(num)
                    })
                  })(i)
                }
                

                输出:0, 1, 2, 3, 4...

                【讨论】:

                • 你能解释一下第一组代码发生了什么吗
                • let 代替var 第一种情况就可以了。
                【解决方案13】:

                javascript中的自调用函数:

                自动调用(启动)自调用表达式,无需调用。自调用表达式在创建后立即被调用。这主要用于避免命名冲突以及实现封装。在此函数之外无法访问变量或声明的对象。为了避免最小化(filename.min)的问题,总是使用自执行函数。

                【讨论】:

                  【解决方案14】:

                  我不敢相信没有一个答案提到隐含的全局变量。

                  (function(){})() 构造不能防止隐含的全局变量,这对我来说是更大的问题,请参阅 http://yuiblog.com/blog/2006/06/01/global-domination/

                  基本上,功能块确保您定义的所有相关“全局变量”都限制在您的程序中,它不会保护您免受定义隐式全局变量的影响。 JSHint 等可以提供有关如何防御这种行为的建议。

                  更简洁的var App = {} 语法提供了类似级别的保护,并且在“公共”页面上时可能被包装在功能块中。 (请参阅 Ember.jsSproutCore 了解使用此构造的库的真实示例)

                  private 属性而言,它们有点被高估了,除非您正在创建公共框架或库,但如果您需要实现它们,Douglas Crockford 有一些好主意。

                  【讨论】:

                  • 严格模式可防止隐含的全局变量。与自动调用程序结合使用可以满足您的要求。我从来不理解私人财产的喧嚣。在 func 构造函数中声明 vars。完毕。如果一想到忘记使用“new”关键字让你夜不能寐,那就写一个工厂函数。再次完成。
                  【解决方案15】:

                  自调用(也称为 自动调用)是当一个函数 立即执行 定义。这是一个核心模式,并且 作为许多人的基础 JavaScript 的其他模式 发展。

                  我是它的忠实粉丝 :) 因为:

                  • 它将代码保持在最低限度
                  • 它强制将行为与表示分离
                  • 它提供了一个防止命名冲突的闭包

                  非常好——(为什么要说它好?)

                  • 它是关于一次定义和执行一个函数。
                  • 您可以让该自执行函数返回一个值并将该函数作为参数传递给另一个函数。
                  • 有利于封装。
                  • 它也适用于块范围。
                  • 是的,您可以将所有 .js 文件包含在一个自执行函数中,并且可以防止全局命名空间污染。 ;)

                  更多here.

                  【讨论】:

                  • 第1点。怎么做?第 2 点。这是完全不同的最佳实践。第3点。什么功能没有? 4,5,6,7。关联? 8. 好吧,我猜 1/8 还不错。
                  • 晚了七年,但对于第 1 点,它根本没有减少代码,实际上它在创建函数时至少添加了两行代码。
                  • 这里唯一的一点是“它提供了一个防止命名冲突的闭包”,其他每一点都是对此的改写或错误。也许你可以简化你的答案?
                  【解决方案16】:

                  有没有参数,“一堆代码”返回一个函数?

                  var a = function(x) { return function() { document.write(x); } }(something);
                  

                  关闭。 something 的值被分配给a 的函数使用。 something 可能有一些不同的值(for 循环),并且每次 a 都有一个新函数。

                  【讨论】:

                  • +1;我更喜欢在外部函数中使用显式的var x = something; 而不是x 作为参数,不过:imo 这样更易读...
                  • @Christoph:如果“某物”的值在函数创建后发生变化,那么它将使用新值,而不是创建时的值。
                  • @stesch:你从哪里得到的?据我所知,情况并非如此。在 JS 中获取真实引用的唯一方法是使用 arguments-object,但即使这样也不适用于所有浏览器
                  • @Christoph:“JavaScript:好的部分”,Douglas Crockford (O'Reilly)
                  • @stesch:它不像您描述的那样工作:如果您删除变量x 并直接依赖于词法范围,即document.write(something),将使用新值。 .
                  【解决方案17】:

                  范围隔离,也许吧。这样函数声明中的变量就不会污染外部命名空间。

                  当然,在一半的 JS 实现中,它们无论如何都会这样做。

                  【讨论】:

                  • 那些会是什么实现?
                  • 任何未以严格模式编写且包含导致其成为全局变量的隐式 var 声明的实现。
                  【解决方案18】:

                  由于 Javascript 中的函数是一等对象,通过这种方式定义它,它有效地定义了一个“类”,很像 C++ 或 C#。

                  该函数可以定义局部变量,并在其中包含函数。内部函数(实际上是实例方法)可以访问局部变量(实际上是实例变量),但它们将与脚本的其余部分隔离。

                  【讨论】:

                    【解决方案19】:

                    IIRC 它允许您创建私有属性和方法。

                    【讨论】:

                      【解决方案20】:

                      命名空间。 JavaScript 的作用域是函数级的。

                      【讨论】:

                      • downvotes 仍然出现,因为我使用了 namespacing 而不是 scoping;这是一个定义问题 - 参见例如Wikipedia计算机科学中的命名空间(有时也称为名称范围)是为保存唯一标识符或符号的逻辑分组而创建的抽象容器或环境(即,名称)。命名空间标识符可以为名称提供上下文(计算机科学中的范围),并且这些术语有时可以互换使用。
                      • Javascript 的函数级作用域提供了变量名所在的空间,一个 namespace;它是一个与命名空间标识符无关的匿名者是无关紧要的......
                      猜你喜欢
                      • 2011-08-30
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-10-19
                      相关资源
                      最近更新 更多