2011-02-02 12 views
7

Mirando algunas bibliotecas de JavaScript y el código de otras personas He visto dos patrones comunes, no sé si hay una diferencia o ventaja en el uso de uno de ellos. Los patrones se ven algo así como esto:¿Hay alguna diferencia entre estos dos patrones de JavaScript?

1.

var app = (function() { 
    // Private vars 

    // Module 
    var obj = { 
     prop: "", 
     method: function() {} 
    }; 

    return obj; 
})(); 

2.

(function() { 
    // Private vars 

    // Module 
    var obj = { 
     prop: "", 
     method: function() {} 
    }; 

    window.app = obj; 
})(); 

Son estos patrones de la misma o hacer uno de ellos tiene una ventaja o uso distinto que el otro?

Gracias de antemano.

+4

Preferiría personalmente el primero ya que no tiene un objeto "codificado" – mplungjan

Respuesta

4

El segundo asume la existencia de un objeto llamado window en el ámbito principal y asigna una propiedad allí.

El primero deja en manos de la persona que realiza la tarea la asignación, y no depende de la definición de un window (que probablemente solo esté dentro de un navegador web).

Por lo tanto, yo diría que el primero es definitivamente mejor (más autónomo, menos dependiente del medio ambiente).

+1

Bueno, muy pocos scripts ECMA (si los hay) son compatibles con navegadores y otras plataformas al mismo tiempo. Así que "asumir" la 'ventana' como objeto global no es un gran error. – jAndy

+0

@jAndy: No sé, he escrito algunas secuencias de comandos que funcionan tanto en el navegador como en otros entornos. Si necesita el objeto global, puede obtenerlo sin usar 'window':' var globalObj = (function() {return this;})(); ' –

-2

La segunda forma tiene una pequeña ventaja ya que tiene una función completamente independiente; por ejemplo, podría tener un encabezado y un pie de página estándar para sus archivos JS.

La parte en la que no estoy completamente vendido es la variable local dentro del bloque. Yo tiendo a preferir esto:

(function() { 
    // Private vars 

    // Module 
    window.app = { 
     prop: "", 
     method: function() {} 
    }; 
})(); 

a pesar de que se rompe un poco cuando estás haciendo más de una cosa, como por ejemplo la construcción de un objeto con múltiples métodos en lugar de un único objeto, como en este ejemplo.

+0

¿Puede explicar por qué es una ventaja para la función ser uno mismo? contenido? Gracias. – jmort253

+0

Di un ejemplo; Realmente es una preferencia personal cuál de los dos patrones usa. No creo que haya ninguna razón técnica por la que uno de los dos patrones sea mejor. – Spyder

+1

Ambas formas son "completamente independientes", como dices. –

0

En el primer ejemplo, si app se define dentro de otra función, app solo estará disponible en ese ámbito local, mientras que en el segundo ejemplo la variable app se asigna explícitamente al ámbito global.

En el segundo ejemplo, app solo se asignará al ámbito global si se define en el ámbito global fuera de funciones.

1

Ambos están logrando lo mismo, para crear un objeto en el espacio de nombre global en el momento en que se ejecuta el código.

Uno no está más "codificado" que el otro, ya que ninguno está haciendo ningún tipo de prototipado de funciones en el que pueda crear clones del objeto con la nueva palabra clave. Es solo una cuestión de preferencia, en mi opinión.

Por ejemplo, jQuery hace algo parecido a éste:

(function(window, undefined) { 

// Use the correct document accordingly with window argument (sandbox) 
var document = window.document; 
var jQuery = (function() { 

// Define a local copy of jQuery 
var jQuery = function(selector, context) { 
     // The jQuery object is actually just the init constructor 'enhanced' 
     return new jQuery.fn.init(selector, context, rootjQuery); 
    }, 

    // Map over jQuery in case of overwrite 
    _jQuery = window.jQuery, 

    // Map over the $ in case of overwrite 
    _$ = window.$, 

... 

Pero Prototype JS Biblioteca hace lo primero:

var Prototype = { 
    Version: '1.6.1', 

    Browser: (function(){ 
    var ua = navigator.userAgent; 
    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; 
    return { 
     IE:    !!window.attachEvent && !isOpera, 
     Opera:   isOpera, 
     WebKit:   ua.indexOf('AppleWebKit/') > -1, 
     Gecko:   ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, 
     MobileSafari: /Apple.*Mobile.*Safari/.test(ua) 
    } 
    })(), 

... 

No sé de ninguna razón por la que uno es mejor que el otro, o que cumplen su tarea de manera diferente (para crear el objeto de la aplicación en el espacio de nombres de la ventana).

2

tl; dr: elija un método y sea consistente.


En mi opinión, el primer método tiene una ligera ventaja para la legibilidad. En mi cabeza, cuando lo leo, veo que "se está definiendo el módulo app" y que todo lo que hay dentro de este cierre pertenece a ese módulo. Esto es una descomposición natural para mí e impone la naturaleza orientada a objetos del módulo a punto de definirse.

Otra razón por la que estoy a favor del primer método es que es más claro cambiar el alcance en el que se define un módulo. Cada módulo que define no necesita ser parte del alcance global. Usando el segundo método, si el alcance no se inyecta pasando un objeto principal como lo ilustra Jared Farrish con su ejemplo jQuery, entonces corre el riesgo de romper su código si decide cambiar el nombre de ese objeto padre. Este ejemplo ilustra el punto:

var namespace = { 
    subns: { ... } 
}; 

(function() { 
    var module = { ... }; 
    namespace.subns.someModule = module; 
}()); 

En cualquier momento los identificadores namespace o subns cambio, también hay que actualizar este módulo y cualquier otro módulo que sigue este patrón y se suma al mismo objeto.

En general, ni el método uno ni el método dos (con inyección de dependencia) es "mejor" que el otro, es simplemente una cuestión de preferencia. El único beneficio que puede obtenerse de esta discusión es que debe seleccionar un método y ser consistente.

Cuestiones relacionadas