¿Qué se entiende por "filtración" en el ámbito global?

Hace un tiempo, ofrecí un patrón de diseño de JavaScript (el Patrón de módulo, ver más abajo) que obtuve de un ejemplo de John Resig como parte de una solución a la pregunta de alguien y recibí el siguiente comentario:

“… Ese patrón está un poco diseñado y no es tan bueno. Todavía se filtra al alcance global. y no se está abriendo a los cargadores asíncronos. ¡Pero es mejor que la codificación ad-hoc! "

Así que ...

Si "filtrar" en el alcance global significa "su objeto se agrega a la ventana del navegador (objeto)" ... entonces todo ya se agrega (globalmente):

Esto se "filtra" al alcance global:

window.jQuery

…solo llama: window.jQuery y se resuelve como una función ();

Esto se "filtra" al alcance global:

function HelloWorld() { alert(‘Howdy’); }

…solo llama: window.HelloWorld() y obtendrás 'Hola'.

Esto se "filtra" al alcance global:

var myVariable = 10;

…solo llama: window.myVariable y obtendrás 10

Si el comentarista está en lo correcto, entonces todo lo anterior "se filtra" en el alcance global. Entonces, personalmente, no veo una manera de NO "filtrar" en el alcance global ya que incluso sus controles de formulario existen allí (también).

Como tal, aquí están mis preguntas ...

  • ¿Qué se entiende por "filtración" en el ámbito global?
  • ¿Por qué es eso malo?
  • ¿Cómo lo evitas?
  • Al querer crear objetos personalizados persistentes, ¿por qué el patrón de módulo (abajo) es malo?
  • Los patrones de diseño le permiten encapsular lógica compleja, ¿La encapsulación es repentinamente mala simplemente porque estamos escribiendo en JavaScript??
  • O ... ¿este comentarista simplemente está equivocado?

Aquí está el patrón de módulo que mencioné anteriormente:

<script type="text/javascript">
    var myNamespace = (function($) {
        var publicInstances = {};

        // ***********************
        // myObject
        publicInstances.myObject = myObject;
        function myObject() {

            /// <summary>A pointer to this</summary>
            var self = this;

            this.someProperty = new String();

            this.initialize = function() {
                /// your code here
            }
            this.someMethod = function() {
                /// your code here
            }

            self.initialize();
        }

        return publicInstances;
    })(jQuery);


    jQuery(document).ready(function() {
        // Use would look like
        var myInstance = new myNamespace.myObject();
    });
</script>


ACTUALIZADO:
Estoy satisfecho con las respuestas a continuación y quiero agradecer a todos por tomarse el tiempo para comentar.

RESUMEN DE LAS RESPUESTAS A CONTINUACIÓN:
La "fuga" en el alcance global ocurre cuando algo utilizado en el alcance local se pone involuntariamente a disposición del alcance global (por ejemplo, el objeto de ventana). Esto es malo porque abre la página a posibles colisiones de nombres que podrían dar como resultado que las variables se resuelvan con valores o tipos inesperados.

Hacer que una variable sea global intencionalmente no se considera una "fuga". Sin embargo, se requiere un espacio de nombres adecuado del objeto para reducir el potencial de dichas colisiones de nombres.

No puede evitar las variables de ámbito global, pero puede reducir los riesgos anteriores mediante el uso de cargadores asíncronos y módulos de definición disponibles en complementos como RequireJS or Curl.

preguntado el 10 de mayo de 11 a las 14:05

El segundo y tercer ejemplo no se "filtran" en el alcance global si no están definidos en el alcance global. Quizás también desee proporcionar un enlace a la respuesta en cuestión. -

Agregué el enlace de arriba ... pero realmente me gustaría que la gente se enfocara en responder ESTA pregunta en lugar de criticar la fuente-pregunta. -

FYI: Voy a dejar que más personas voten esto antes de que marque la respuesta:

"No puede evitar las variables de ámbito global" - si quiere decir "no puede escribir código JavaScript que no cree al menos una variable de ámbito global", no creo que eso sea cierto. (P.ej (function () {var geoff='geoffdegeoff'; alert('local: ' + geoff);})(); alert('global:' + window.geoff);). Pero creo que quiere decir que, en la práctica, la mayoría de JavaScript que escriba probablemente querrá crear al menos una variable de ámbito global. -

4 Respuestas

[[Cuento]]

No cree variables globales nunca y use un cargador de módulo asíncrono como requirejs or rizo

[[Larga historia]]

Ese comentario estaba mal estructurado.

No hay nada de malo en el sistema de módulos. Me estaba quejando sobre el uso de variables globales. (Sigo pensando que el patrón de módulo genérico completo está inflado).

Si debe evitar todas las variables globales es una cuestión diferente y creo que es una cuestión de estilo. Puede usar un cargador asíncrono para pasar módulos o usar window para pasar los módulos.

  • ¿Qué se entiende por "filtración" en el ámbito global?

Lo que quise decir fue su creación de variables globales. Minimizar el uso de variables globales es un patrón. En la programación de estilo funcional, es posible tener cero variables globales, pero este es un patrón diferente al uso de módulos globales.

  • ¿Por qué es eso malo?

Tener cualquier estado a nivel mundial puede hacer que ese estado se corrompa.

  • ¿Cómo lo evitas?

No puedes. Sin embargo, puede minimizar la cantidad de variables globales. Para evitar tener un estado global por completo, puede utilizar cargadores asincrónicos. Estos definen algunas variables globales para usted que luego puede usar.

  • Al querer crear objetos personalizados persistentes, ¿por qué el patrón de módulo (abajo) es malo?

No hay nada de malo en el patrón del módulo. El problema es almacenar su módulo a nivel mundial. El problema es tener espacios de nombres globales.

  • Los patrones de diseño le permiten encapsular lógica compleja, ¿la encapsulación de repente es mala simplemente porque estamos escribiendo en JavaScript?

Ahora que he aclarado la intención del comentario, esta pregunta no es realmente relevante.

  • O ... ¿este comentarista simplemente está equivocado?

En el mejor de los casos, el comentario estaba mal redactado. Me opuse a los espacios de nombres globales en lugar de a los módulos, pero no lo dije correctamente.

La alternativa es utilizar cargadores asincrónicos y definir módulos. Estos se pueden reducir a dos variables globales. define y require.

require = function(moduleName, callback)

Esto obtendrá un módulo y luego se lo devolverá.

define = function(obj)

esto define un módulo.

El concepto aquí es que el código de múltiples archivos es el siguiente:

// main.js
require([
  "foo.js",
  "bar.js",
  ...,
], function(foo, bar, ...) {
   // do stuff
}); 

//foo.js

(function() {
    var namespace = modulePatternCode;
    ...
    define(namespace):
})();

//bar.js 

(function() {
    var namespace = modulePatternCode;
    ...
    define(namespace):
})();

Respondido el 21 de diciembre de 12 a las 18:12

+1 Al usar un cargador de módulos, todo (excepto el cargador de módulos en sí mismo quizás) se puede contener dentro de los cierres. - Ates Goral

@AtesGoral sí. Necesita dos objetos globales para el cargador de módulos. - Raynos

¿Sería posible publicar un ejemplo de cargador? - Prisionero CERO

¡Ajá! @Raynos fue el culpable. Eso lo explica todo. - Andy E

@AndyE ¡Oi! Qué cruel. @Prisionero requirejs tiene la sintaxis exacta (más o menos) que mencioné. También tiene un conjunto completo de herramientas de soporte para usarlo como cargador asíncrono. - Raynos

"Fugas" en el alcance global es cuando algo utilizado en un alcance local se pone involuntariamente a disposición del alcance global. Eso significa asignar a una variable que aún no está definida en el alcance actual:

function myFunction() {
    a=1;
}

myFunction();
alert(a);
//-> 1

Es malo porque podría haber colisiones de nombres que resulten en variables con valores / tipos diferentes a los esperados. También puede provocar un error en los exploradores de Internet más antiguos cuando se olvida de utilizar el var palabra clave para una variable utilizada en un for declaración.

No clasificaría intencionalmente hacer una variable global como "fuga", porque es más como si la estuviera "vertiendo" en el ámbito global. Sin embargo, algunos todavía consideran que esto es una mala práctica (aunque creo que es un poco melodramático) porque todavía hay posibles colisiones de nombres con las propiedades actuales del window objeto o variables establecidas por otros scripts y bibliotecas.

Respondido el 21 de diciembre de 12 a las 19:12

Estoy de acuerdo con su uso de la palabra "no intencional". En mi opinión, creo que TODOS los objetos personalizados (persistentes) deben encapsularse (como se muestra). Pero "podría" estar equivocado ... por eso pregunté. - Prisionero CERO

¿Puede aclarar lo que quiere decir con "verter"? - Prisionero CERO

@ Prisionero: Lo digo en serio en la forma en que "gotear" es algo que sucede involuntariamente, "verter" es algo que haces intencionalmente. Solo trato de mantener la analogía líquida :-) - Andy E

sí, ese comentario fue melodramático;) ¡aunque los globales son malos! todos ellos. - Raynos

Su módulo solo "filtra" su espacio de nombres, por lo que es bastante aceptable.

contestado el 10 de mayo de 11 a las 18:05

En realidad, esa es también mi impresión. En mi opinión, esta es la esencia de la encapsulación. Pero ... "podría" estar equivocado ... por eso estoy preguntando. - Prisionero CERO

el titular del espacio de nombres debería "filtrarse", por eso se utiliza como espacio de nombres. =) - Headshota

Sí, a menos que te conectes a un espacio de nombres existente, no veo cómo no puedes "filtrar" al menos un identificador. - Jad

es por eso que se recomienda usar un estilo de nomenclatura como lo hace Java, usando el nombre de dominio de uno, por ejemplo, com_mydomain. - Headshota

El titular del espacio de nombres no tiene que "filtrarse", si está utilizando un cargador de módulos. Todo (excepto las funciones propias del cargador de módulos) se puede contener dentro de cierres. - Ates Goral

Ejemplo de cargador usando RequireJS:

Defina un módulo de utilidades en utils.js:

define(function () {
    return {
        each: function (iterable, callback) {
            // ...
        },
        map: function (iterable, mapper) {
            // ...
        }
    };
});

Utilice el módulo anterior en otro módulo, digamos math.js:

define([ "utils" ], function (utils) {
    return {
        sum: function (numbers) {
            var sum = 0;

            utils.each(numbers, function (n) {
                sum += n;
            });

            return sum;
        },
        average: function (numbers) {
            return this.sum(numbers) / numbers.length;
        }
    };
});

Y puede usar math.js en otro archivo, digamos main.js:

console.log("About to add 1-3");

require([ "math" ], function (math) {
    console.log(math.sum([ 1, 2, 3 ]));
});

Todavía puede tener espacios de nombres y mantenerlos cálidos y acogedores dentro de los módulos:

namespace.js:

define([ "foo", "bar", "moo" ] function (foo, bar, moo) {
    return {
        foo: foo,
        bar: bar,
        moo: moo
    };
});

Luego, el resto de los módulos pueden usar este espacio de nombres durante la definición:

define([ "namespace" ], function (namespace) {
    namespace.foo(42);
});

O en tiempo de ejecución, en algún otro módulo:

define(function () {
    return {
        initialize: function () {
            require([ "namespace" ], function (namespace) {
                namespace.foo(42);
            });
        }
    };
});

En los usos anteriores, nada más que define y require son globales. Por supuesto, estos son solo ejemplos ilustrativos, ya que hay muchas formas diferentes de definir / usar módulos en RequireJS.

contestado el 10 de mayo de 11 a las 22:05

Recuerda que callFoo debe estar envuelto en un cierre. Tampoco debería tener funciones públicas en el ámbito global. - Raynos

@Raynos: Ese extracto estaba destinado a ser de algún módulo. Lo convertiré en un módulo para aclarar el punto. - Ates Goral

Conozco el significado, simplemente señalando que podría malinterpretarse. Solo quería aclarar que debes evitar tanto global var declaraciones y declaraciones de funciones globales - Raynos

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.