【问题标题】:Enclosing external jQuery script封闭外部 jQuery 脚本
【发布时间】:2011-09-12 14:23:46
【问题描述】:

我有一个外部 JavaScript 文件,该文件将用于包含许多其他脚本的页面。我的脚本涉及很多监听事件的 jQuery,并且根据设计,我声明了许多全局变量。我一直在阅读最佳实践文章,其中有很多关于“污染全局命名空间”和无意的脚本交互的说法。

封装(封装?)我的 JavaScript 文件的最佳方法是:

  • 我仍然可以访问一些 外壳外的变量
  • jQuery 事件监听器将 正常运行

我无权透露代码,因此即使是一般性的回复也值得赞赏。此外,欢迎提供有关使脚本不易受到页面上其他脚本攻击的任何其他提示。

我已经找到了常规 JavaScript 的封闭样式,但是使用 jQuery 会使这变得复杂吗?

【问题讨论】:

  • 我认为最好的方法是简单地使用对象或使用命名空间

标签: javascript jquery reliability side-effects


【解决方案1】:

这是 jQuery 插件的常见做法,原因与您提到的相同:

;(function ($) {

    /* ... your code comes here ... */

})(jQuery);

这是一个即时函数。如果你在里面声明你的“全局”变量,它们将是这个闭包的本地变量(对于你在里面创建的代码仍然是“全局的”)。您的事件侦听器也将在这里工作,您仍然可以访问真正的全局变量。

【讨论】:

  • 感谢您的建议,但我需要能够访问外壳之外的某些变量。
  • 在这种情况下什么会阻止你?
  • “他们将在这个闭包中本地”
【解决方案2】:

通常归结为将您的对象封装到“命名空间”中。我在此处使用引号是因为该术语不是 JavaScript 中的官方语义,而是通过基本对象封装实现的。

有几种方法可以做到这一点,最终归结为个人喜好。

一种方法是只使用一个基本的 JS 对象,并将所有内容保存在其中。对象的名称应该是语义化的,并赋予对象一些含义,否则它的目的就是包装您自己的代码并将其置于全局命名空间之外。

var SomeName = {
    alpha: 1,
    beta: {a: 1, b: 2},
    gamma: function(){ 
        SomeName.alpha += 1;
    }
}

在这种情况下,只有 SomeName 位于全局命名空间中。这种方法的一个缺点是命名空间内的所有内容都是公共的,您必须使用完整的命名空间来引用一个对象,而不是使用 'this' - 例如在 SomeName.gamma 中,我们必须使用 SomeName.alpha 来引用 alpha 的内容。

另一种方法是使您的命名空间成为具有属性的函数。这种方法的优点是您可以通过闭包创建“私有”变量。它还允许您访问封闭的函数和变量,而无需完整的命名空间引用。

var SomeName = (function(){
   var self = this;
   var privateVar = 1;
   var privateFunc = function() { };   

   this.publicVar = 2;
   this.publicFunc = function(){
       console.log(privateVar);
       console.log(this.publicVar); // if called via SomeName.publicFunc

       setTimeout(function(){ 
           console.log(self.publicVar);
           console.log(privateVar);
       }, 1000);
   };
}();

这种方法的另一个好处是它可以保护您想要使用的全局变量。例如,如果您使用 jQuery 和另一个创建 $ 变量的库,您可以始终确保在通过这种方法使用 $ 时引用 jQuery:

var SomeName = (function($){
    console.log($('div'));
})(jQuery);

【讨论】:

    【解决方案3】:

    您可以将您的代码包装在一个匿名的 Javascript 函数中,并且只返回您想向外界公开的内容。您需要在全局变量前加上 var 前缀,以便它们仅保留在匿名函数的范围内。像这样的:

    var myStuff = (function() {
    
       var globalVar1;
       var globalVar2;
       var privateVar1;
    
       function myFunction() {
         ...
       }
    
       function myPrivateFunction() {
         ...
       }
    
       return {
          var1: globalVar1,
          var2: globalVar2,
          myFunction: myFunction
       };
    
    })();
    

    现在您可以访问myStuff.var1myStuff.myFunction()

    【讨论】:

      【解决方案4】:

      我刚开始使用RequireJS,现在已经迷上了它。

      它基本上是一个模块化 JavaScript 格式的依赖管理系统。通过这样做,您几乎可以消除将任何东西附加到全局命名空间。

      很好的是,您只在页面require.js 上引用一个脚本,然后告诉它首先运行哪个脚本。从那里开始,一切都是魔法......

      这是一个示例实现脚本:

      require([
         //dependencies
         'lib/jquery-1.6.1'
      ], function($) {
         //You'll get access to jQuery locally rather than globally via $
      
      
      });
      

      通读 RequireJS API,看看这是否适合你。我现在正在写我所有的脚本。这很棒,因为在每个脚本的顶部,您确切地知道您的依赖项类似于服务器端语言 - Java 或 C#。

      【讨论】:

      • 嗯,我对此很感兴趣。根据您的经验,需要对页面进行多少修改(可能使用一些单独的外部脚本文件、一些内联脚本)才能使其正常工作?容易吗?
      • @chris 采用还不错,外部脚本文件都使用define()函数将它们注册为模块。然后你的实现页面只引用你定义的那些模块。我可能会在下周左右写一篇关于它的博客文章。我一定会在这里发回链接。
      【解决方案5】:

      两种封装或限制命名空间污染的方法

      1) 创建一个全局变量并将您需要的所有内容填入其中。

      var g = {};
      g.somevar = "val";
      g.someothervar = "val2";
      g.method1 = function() 
      {
         // muck with somevar
         g.somevar = "something else";
      };
      

      2) 对于内联脚本,考虑限制调用函数的范围。

      <script>
      (  
         function(window)
         {
            // do stuff with g.somevar
            if(g.somevar=="secret base")
              g.docrazystuff();      
      
         }
      )();  // call function(window) then allow function(window) to be GC'd as it's out of scope now
      </script>
      

      【讨论】:

        【解决方案6】:

        一种方法是这样命名空间:

        var MyNamespace = {
            doSomething: function() {},
            reactToEvent: function() {},
            counter: 0
        }
        

        您只需使用命名空间引用函数或变量:MyNamespace.reactToEvent。这可以很好地分离您通常在window 中拥有的东西(所有的对抗都在那里)。

        【讨论】:

        • 这绝对是最简单的方法 - 创建您自己的全局属性,将所有内容置于其中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-15
        • 1970-01-01
        • 2021-04-25
        • 1970-01-01
        • 2011-10-18
        • 2015-03-06
        • 1970-01-01
        相关资源
        最近更新 更多