JavaScript库的代码模式

JavaScript库的代码模式,javascript,design-patterns,Javascript,Design Patterns,在JavaScript库(jQuery、MathJax、Markdown渲染库等)中,我经常看到各种模式: 模式#1 index.html ... <script src="mylibrary.js"></script> </body> </html> <html> <head> ... <script src="mylibrary.js"></script> </head> <b

在JavaScript库(jQuery、MathJax、Markdown渲染库等)中,我经常看到各种模式:

模式#1 index.html

...
<script src="mylibrary.js"></script>
</body>
</html>
<html>
<head>
...
<script src="mylibrary.js"></script>
</head>
<body>
...
</body>
</html>
<html>
<head>
...
<script src="mylibrary.js"></script>
</head>
<body>
...
<script>
mylibrary.run();    // explicitly needs to be run
</script>
</body>
</html>

模式#2 index.html

...
<script src="mylibrary.js"></script>
</body>
</html>
<html>
<head>
...
<script src="mylibrary.js"></script>
</head>
<body>
...
</body>
</html>
<html>
<head>
...
<script src="mylibrary.js"></script>
</head>
<body>
...
<script>
mylibrary.run();    // explicitly needs to be run
</script>
</body>
</html>

...
...
相同的mylibrary.js

var mylibrary = (function() {
    ...
    var myVar = ...
    ...
})();

模式#3 index.html

...
<script src="mylibrary.js"></script>
</body>
</html>
<html>
<head>
...
<script src="mylibrary.js"></script>
</head>
<body>
...
</body>
</html>
<html>
<head>
...
<script src="mylibrary.js"></script>
</head>
<body>
...
<script>
mylibrary.run();    // explicitly needs to be run
</script>
</body>
</html>

...
...
mylibrary.run();//显式地需要运行
相同的mylibrary.js

var mylibrary = (function() {
    ...
    var myVar = ...
    ...
})();
问题:
  • var mylibrary=(function(){…})()的用途是什么技巧

  • 模式1和模式2之间有区别吗?(我会说不)

  • 有什么理由选择3而不是2吗?
    看起来这两个都很有趣。例如:模式#2是有意义的(它将自动划分DOM并呈现数学公式),这是他们选择的(请参阅)。模式#3,要求实际运行渲染的操作也有意义

  • 其目的是创建一个模块化(立即调用的函数表达式)。这有助于避免污染全局名称空间/为全局名称空间分配变量,并有助于防止与应用程序中可能出现的其他外部插件/依赖项发生冲突

  • 对。在第一种情况下,脚本加载在头部,而脚本加载在#2中主体关闭之前。建议在关闭主体之前加载脚本,这样页面在加载javascript文件时不会挂起

  • 3比2。至于原因,请参考第1点

  • 更新

    下面是一个示例,演示了如果只在全局名称空间中分配一个变量,情况会如何

    var exampleGlobal = "I'm an example global variable";
    
    function one() {
      exampleGlobal = 1;
    }
    
    ....
    
    function two() {
      exampleGlobal.charAt(0);
    }
    
    one(); // Changes the exampleGlobal to a Number
    two(); // Tries to use a string-based instance method but throws an error since exampleGlobal is now a number
    
    在这里,我们看到
    exampleGlobal
    已公开并可用于此脚本中的所有函数。在这种情况下,在脚本中的任何位置都很容易出错并更改此变量的值/用途。这里是生命发挥作用的地方:

    var exampleModule = (function() {
      var privatized = "I'm only accessible within this module";
    
      return {
         privatized: privatized
      }
    }());
    
    在上面,我们只创建了一个全局变量,作为一个模块,并封装了只与它相关的值/函数。尝试全球私有化。您会发现,除非显式调用该模块及其返回值,否则它将返回undefined,因为它在全局范围内不可用:

    var exampleModule = (function() {...}());
    
    console.log(privatized); // undefined
    console.log(exampleModule.privatized); // returns: "I'm only accessible within this module"
    
    iLife的优势还在于,您可以安全地避免应用程序中可能出现的命名冲突。例如,jQuery和Mootools共享
    $
    字符来调用各自的函数调用。如果希望安全地使用jQuery而不与Mootools发生任何冲突,可以将
    $
    对象作为参数传递到IIFE中,这样将使其不受全局范围的影响

    (function($) { // Here this is just the parameter we'd like to refer jQuery as
      $('div');
    }(jQuery)); // You pass in the actual jQuery object here which is when/where the function is immediately invoked.
    
  • 其目的是创建一个模块化(立即调用的函数表达式)。这有助于避免污染全局名称空间/为全局名称空间分配变量,并有助于防止与应用程序中可能出现的其他外部插件/依赖项发生冲突

  • 对。在第一种情况下,脚本加载在头部,而脚本加载在#2中主体关闭之前。建议在关闭主体之前加载脚本,这样页面在加载javascript文件时不会挂起

  • 3比2。至于原因,请参考第1点

  • 更新

    下面是一个示例,演示了如果只在全局名称空间中分配一个变量,情况会如何

    var exampleGlobal = "I'm an example global variable";
    
    function one() {
      exampleGlobal = 1;
    }
    
    ....
    
    function two() {
      exampleGlobal.charAt(0);
    }
    
    one(); // Changes the exampleGlobal to a Number
    two(); // Tries to use a string-based instance method but throws an error since exampleGlobal is now a number
    
    在这里,我们看到
    exampleGlobal
    已公开并可用于此脚本中的所有函数。在这种情况下,在脚本中的任何位置都很容易出错并更改此变量的值/用途。这里是生命发挥作用的地方:

    var exampleModule = (function() {
      var privatized = "I'm only accessible within this module";
    
      return {
         privatized: privatized
      }
    }());
    
    在上面,我们只创建了一个全局变量,作为一个模块,并封装了只与它相关的值/函数。尝试全球私有化。您会发现,除非显式调用该模块及其返回值,否则它将返回undefined,因为它在全局范围内不可用:

    var exampleModule = (function() {...}());
    
    console.log(privatized); // undefined
    console.log(exampleModule.privatized); // returns: "I'm only accessible within this module"
    
    iLife的优势还在于,您可以安全地避免应用程序中可能出现的命名冲突。例如,jQuery和Mootools共享
    $
    字符来调用各自的函数调用。如果希望安全地使用jQuery而不与Mootools发生任何冲突,可以将
    $
    对象作为参数传递到IIFE中,这样将使其不受全局范围的影响

    (function($) { // Here this is just the parameter we'd like to refer jQuery as
      $('div');
    }(jQuery)); // You pass in the actual jQuery object here which is when/where the function is immediately invoked.
    
  • 您无法访问
    myVar
    外部
    mylibrary
  • 模式#3使您能够更好地控制实际使用mylibrary的时间和方式
  • 您无法访问
    myVar
    外部
    mylibrary
  • 模式#3使您能够更好地控制实际使用mylibrary的时间和方式

  • 谢谢@lexicore。如果我把脚本放在
    中,如果这个脚本使用iLife,它会在DOM完成后执行还是在加载后立即执行?@Basj通过在注释中提出更多的问题,你已经足够宽泛了。你自己做一个快速搜索怎么样?例如,为了找到类似这样的内容。我最后做了@lexicore,发现即使我使用iLife,如果
    在实际正文内容之前,它也无法查看此正文内容。谢谢@lexicore。如果我把脚本放在
    中,如果这个脚本使用iLife,它会在DOM完成后执行还是在加载后立即执行?@Basj通过在注释中提出更多的问题,你已经足够宽泛了。你自己做一个快速搜索怎么样?例如,我最终找到了@lexicore,发现即使我使用iLife,如果
    在实际正文内容之前,它也无法查看此正文内容。请参阅我更新的ans