2012-02-14 28 views
26

Quiero crear módulos para estructurar mi aplicación NodeJS, pero estoy un poco perdido, y no he encontrado nada (con horas de búsqueda) que sea completamente definitivo sobre el tema .Estructurando un módulo NodeJS - variables y métodos

decir te gustaría crear un módulo de "usuario", de la que puede crear nuevos usuarios en mi código usando algo como:

var newUser = new User(); 

Idealmente, me requiero mi módulo en la parte superior de mi código usando algo como:

var User = require('../lib/user'); 

Esto funciona genial. La pregunta es, ¿cómo debería estructurar el módulo de usuario? ¿Es la siguiente la mejor manera?

module.exports = function User() { 
    var authorized = false; 
    var username = undefined; 
    var password = undefined; 
    var statistics = undefined; 

    this.authorized = function() { 
     return authorized; 
    } 
    this.username = function() { 
     return username; 
    } 
    this.statistics = function() { 
     return statistics; 
    } 
} 

estoy escribiendo captadores y definidores de mis diversas variables de módulo, lo que me permite ocultar las cosas que no quiero para acceder accidentalmente de otro código. Sin embargo, lo he hecho de esta manera antes:

function User() { 
    this.authStatus = false; 
    this.email; 
    this.displayName; 
    this.inSession; 
} 

User.prototype.isAuthenticated = function() { 
    return(this.authStatus && this.email && this.displayName) 
} 

User.prototype.isInSession = function() { 
    return(this.inSession && this.isAuthenticated()); 
} 

exports.User = User; 

Esto también funciona, con una advertencia; No he encontrado una forma de acceder a las propiedades del usuario desde dentro de los cierres. Si mi comprensión es correcta, con la segunda implementación, no puedo. Esto significa que si necesito entregar una función a una biblioteca de db como una devolución de llamada para editar las propiedades del usuario, no puedo. Eso sería algo como:

User.prototype.login = function() { 
    db.doDbStuff('get user data query', function(_error, _result) { 
     this.username = _result[0].name; //this code will not work 
    }); 
} 

El código no funciona, a mi entender, debido a que la palabra "this" está dentro del alcance del cierre, no el usuario. Incluso si el código fuera colocado dentro de la función de Usuario:

function User() { 
    this.login = function() { //you know 

No funcionaría.

Supongo que mi pregunta es, ¿cuál es la mejor solución para este problema? ¿Es el método que presenté en el primer bloque de código? Eso parece bastante engorroso y sucio y propenso a una colisión variable. Estoy asustado.

¡Gracias de antemano!

+2

Si algo de esto no tiene sentido, me disculpo de antemano, acabo de tomar algunos medicamentos para el dolor para mis muelas del juicio y me siento un poco liviano y funky. –

+1

Parece sangrar más en el ámbito de la arquitectura javascript en lugar de hacer con node.js en general. http://speakerdeck.com/u/addyosmani/p/scaling-your-javascript-applications y http://www.slideshare.net/SlexAxton/superclassy-inheritance-in-javascript son bastante buenos. – balupton

+0

Vale la pena señalar que en ambos ejemplos las variables NO SON privadas. Deberías echarle un vistazo a: http://javascript.crockford.com/private.html –

Respuesta

24

Normalmente voy con el segundo enfoque, adjuntando funciones a los prototipos.

El problema que está teniendo con las variables "no está disponible en cierres" no tiene nada que ver con los prototipos. Tendría el mismo problema en cualquier forma que lo estructura.

Tiene que ver con la de JavaScript tantas veces confuso dinámica this: http://robotlolita.me/2011/10/09/understanding-javascript-oop.html#sec-2-1

Básicamente, tiene que hacer algo como:

User.prototype.login = function() { 
    var self = this // bind this to self so you have a reference to what `this` was 

    db.doDbStuff('get user data query', function(_error, _result) { 
     self.username = _result[0].name; // self refers to the original `this`, so this works. 
    }); 
} 

También tiene la opción de utilizar la función.aprieto: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

Dentro de la función límite, el valor de this será cualquier valor que ha proporcionado a .bind(value):

User.prototype.login = function() { 
    db.doDbStuff('get user data query', (function(_error, _result) { 
     this.username = _result[0].name; // bind fixes the original `this`, so this also works. 
    }).bind(this)); 
} 

Ya sea que utilice function.bind o self = this es algo así como una pregunta gusto personal, pero que hacíamos algunos puntos de referencia en el freenode # nodejs el otro día y descubrieron bind() es 20 veces más lento que var self = this.

En cuanto a su pregunta original sobre cómo estructurar módulos, hay tantos ejemplos de los que aprender en github. Simplemente encuentre su módulo favorito e inspeccione cómo lo estructuran. Observo que muchas personas prefieren las fábricas para exponer constructores directamente (por ejemplo, require('module').create()). Tu llamada.

+1

Oh. ¡Eso es tan brillante y simple! Jeeze. -- ¡Gracias! –

+0

La dinámica esto es realmente confuso cuando cambia inesperadamente, pero también es una de las características más poderosas de Javascript cuando realmente la usas a propósito. – timoxley

+0

Sí, Javascript me da una sensación muy extraña de extremadamente poderosa e increíble combinada con "oh Dios mío esto es ridículo". - ¿Hay alguna manera en la que recomiendas mantener privados a algunos miembros usando el método prototipo? –

2
User.prototype.login = function() { 
    var _this = this; 
    db.doDbStuff('get user data query', function(_error, _result) { 
     _this.username = _result[0].name; //this code will now work 
    }); 
} 

El 'this' que utilizó estaba fuera de su alcance, que era la devolución de llamada 'this'.

12

Como enfoque diferente, soy seguidor del siguiente patrón.

 
module.exports = function User(data) { 

    //this properly private stuff. 

    var privateVar; 
    var username = data.username; 
    var pass = data.pass; //etc 

    function privateFunc() { 
    } 

    return { 
     login: function(cb) { 
      db.doStuff(username, pass, cb); 
     } 
    }; 
}; 
+4

Me gusta esto también. Pero este enfoque solo funciona para singletons, ¿verdad? No puedes instanciar de esta manera. – owzim

+2

@owzim Este patrón en realidad no es solo para singletons, se ve muy similar a module.exports = (function() {...})(); que es un patrón único porque ejecuta la función directamente. Pero el código en esta respuesta no es un singleton, porque ahora puede hacer require ('data') (my_instance_data) .myInstanceFunction(); – Vegard

Cuestiones relacionadas