Tengo un archivo JavaScript externo que se usará en páginas con muchos otros scripts. Mi script implica una gran cantidad de jQuery que escucha eventos, y por diseño, tengo muchos vars globales declarados. He estado leyendo artículos de mejores prácticas, y se dice mucho sobre 'contaminar el espacio de nombres global' y la interacción inadvertida de scripts.

¿Cuál es la mejor manera de encerrar (encapsular) mi archivo JavaScript para que:

  • Todavía puedo acceder a algunas de las variables fuera del recinto
  • Los oyentes de eventos jQuery funcionarán correctamente

No estoy en libertad de revelar el código, por lo que se agradecen incluso las respuestas generales. Además, cualquier otro consejo sobre cómo hacer que los guiones sean menos vulnerables a otros guiones en la página son bienvenidos.

He encontrado estilos de gabinete para JavaScript normal, pero ¿el uso de jQuery complica esto?

3
user686605 10 jun. 2011 a las 19:19

6 respuestas

La mejor respuesta

En general, esto se reduce a encapsular sus objetos en un "espacio de nombres". Utilizo citas allí porque el término no es una semántica oficial en JavaScript, sino que se logra mediante la encapsulación básica de objetos.

Hay varias formas de hacer esto y, en última instancia, se reduce a preferencias personales.

Un enfoque es usar un objeto JS básico y mantener todo en él. El nombre del objeto debe ser semántico y darle algo de significado al objeto, pero de lo contrario, su propósito es simplemente envolver su propio código y mantenerlo fuera del espacio de nombres global.

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

En este caso, solo SomeName está en el espacio de nombres global. El único inconveniente de este enfoque es que todo lo que está dentro del espacio de nombres es público, y debe usar el espacio de nombres completo para hacer referencia a un objeto, en lugar de usar 'esto', p. en SomeName.gamma tenemos que usar SomeName.alpha para hacer referencia a los contenidos de alpha.

Otro enfoque es hacer que su espacio de nombres sea una función con propiedades. La buena característica de este enfoque es que puede crear variables 'privadas' a través de cierres. También le da acceso a funciones y variables cerradas sin referencias completas del espacio de nombres.

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

La otra ventaja de este enfoque es que le permite proteger las variables globales que desea usar. Por ejemplo, si usa jQuery, Y otra biblioteca que crea una variable $, siempre puede asegurarse de que hace referencia a jQuery cuando usa $ por este enfoque:

var SomeName = (function($){
    console.log($('div'));
})(jQuery);
1
Matt 10 jun. 2011 a las 15:42

Esta es una práctica común con los complementos jQuery por las mismas razones que mencionas:

;(function ($) {

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

})(jQuery);

Esta es una función inmediata. Si declara sus variables "globales" dentro, serán locales para este cierre (aún "globales" para el código que cree dentro). Sus oyentes de eventos también trabajarán aquí, y aún podrá alcanzar variables globales reales.

0
aorcsik 10 jun. 2011 a las 15:52

Dos formas de encapsular o limitar la contaminación del espacio de nombres

1) Crea una var global y agrega todo lo que necesitas en ella.

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

2) Para los scripts en línea, considere limitar el alcance de las funciones llamadas.

<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>
0
mrk 10 jun. 2011 a las 15:27

Puede envolver su código en una función Javascript anónima y solo devolver lo que desea exponer al mundo exterior. Deberá colocar el prefijo var en sus variables globales para que permanezcan solo en el ámbito de la función anónima. Algo como esto:

var myStuff = (function() {

   var globalVar1;
   var globalVar2;
   var privateVar1;

   function myFunction() {
     ...
   }

   function myPrivateFunction() {
     ...
   }

   return {
      var1: globalVar1,
      var2: globalVar2,
      myFunction: myFunction
   };

})();

Ahora puede acceder a myStuff.var1 y myStuff.myFunction().

1
Vivin Paliath 10 jun. 2011 a las 15:29

Un método es el espacio de nombres como este:

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

Solo tendrá que referirse a las funciones o variables utilizando el espacio de nombres: MyNamespace.reactToEvent. Esto funciona bien para separar lo que normalmente tendría en window (donde está toda la confrontación).

1
natedavisolds 10 jun. 2011 a las 15:27

Acabo de empezar a usar RequireJS y ahora me he obsesionado con eso.

Básicamente es un sistema de gestión de dependencias en un formato modular de JavaScript. Al hacerlo, puede eliminar virtualmente adjuntar cualquier cosa al espacio de nombres global.

Lo bueno es que solo hace referencia a un script en su página require.js y luego le dice qué script ejecutar primero. A partir de ahí, todo es magia ...

Aquí hay un script de implementación de ejemplo:

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


});

Lea la API RequireJS y vea si esto es adecuado para usted. Estoy escribiendo todos mis guiones como este ahora. Es genial porque en la parte superior de cada script sabes exactamente qué dependencias son similares a los lenguajes del lado del servidor: Java o C #.

0
John Strickler 10 jun. 2011 a las 15:28