javascript - paths - Con RequireJS, ¿cómo puedo pasar objetos globales o singletons?
requirejs tutorial (6)
Aquí hay una versión donde el módulo en sí es la variable compartida en lugar de una variable dentro de ese módulo.
define(''foo'', [], {bar: "this text will be overwritten"});
define(''bar'', ["foo"], function (foo) {
return {
setBarOnFoo: function () { foo.bar = "hello"; }
};
});
define(''baz'', ["foo"], function (foo) {
return {
setBazOnFoo: function () { foo.baz = "goodbye"; }
};
});
require(["foo", "bar", "baz"], function (foo, bar, baz) {
bar.setBarOnFoo();
baz.setBazOnFoo();
$(''#results'').append(foo.bar + '' '' + foo.baz);
});
// reads: hello goodbye
Digamos que estoy escribiendo código en el nivel de la página principal y 2 dependencias requieren la misma instancia de un objeto y también declaran eso como una dependencia. ¿Cuál es la forma adecuada de hacer esto?
Básicamente, lo que quiero hacer es decir: "Si esta dependencia no está cargada ... entonces cárguela. De lo contrario, use la misma instancia que ya estaba cargada y simplemente pase esa".
Combinando las preocupaciones de Raynos sobre la encapsulación con la aclaración del OP de que quiere exponer un par de métodos en un servicio de mensajería, esta es la forma correcta de hacerlo:
// In messagingServiceSingleton.js
define(function () {
var messagingService = new MessagingService();
return {
notify: messagingService.listen.bind(messagingService),
listen: messagingService.notify.bind(messagingService)
};
});
// In bar.js
define(["./messagingServiceSingleton"], function (messagingServiceSingleton) {
messagingServiceSingleton.listen(/* whatever */);
}
// In baz.js
define(["./messagingServiceSingleton"], function (messagingServiceSingleton) {
messagingServiceSingleton.notify(/* whatever */);
}
Function.prototype.bind
no estará presente en todos los navegadores, por lo que deberá incluir un polyfill como el que proporciona Mozilla .
Un enfoque alternativo (y en mi opinión probablemente mejor) sería convertir el objeto de servicio de mensajería en un módulo. Esto se vería algo así como
// In messagingService.js
define(function () {
var listenerMap = {};
function listen(/* params */) {
// Modify listenerMap as appropriate according to params.
}
function notify(/* params */) {
// Use listenerMap as appropriate according to params.
}
return {
notify: notify
listen: listen
};
});
Ya que expone los mismos métodos de notify
y listen
a todos los que usan su módulo, y estos siempre se refieren a la misma variable de listenerMap
private , esto debería hacer lo que quiera. También evita la necesidad de Function.prototype.bind
y elimina la distinción bastante innecesaria entre el servicio de mensajería en sí y el módulo que impone el uso de singleton.
Como una variación de la respuesta de Domenic, puede usar el módulo mágico ''exportaciones'' para generar automáticamente una referencia para el módulo: "Las propiedades agregadas al objeto de exportaciones estarán en la interfaz pública del módulo, sin necesidad de devolver ningún valor. " Esto evita tener que llamar a la función getTheFoo()
para obtener una referencia.
// In foo.js
define([''exports''], function (foo) {
foo.thereCanBeOnlyOne = true;
});
// In bar.js
define(["exports", "./foo"], function (bar, foo) {
bar.setBarOnFoo = function () { foo.bar = "hello"; };
});
// in baz.js
define(["exports", "./foo"], function (baz, foo) {
baz.setBazOnFoo = function () { foo.baz = "goodbye"; };
});
// In any other file
define(["./foo", "./bar", "./baz"], function (foo, bar, baz) {
bar.setBarOnFoo();
baz.setBazOnFoo();
assert(foo.bar === "hello");
assert(foo.baz === "goodbye");
assert(foo.thereCanBeOnlyeOne);
});
Para abordar el comentario a continuación, personalmente he encontrado que la convención anterior es útil. Su millaje puede variar, pero no dude en adoptar la convención si cree que es útil. La convención se reduce a estas dos reglas:
- Declare ''exporta'' como la primera dependencia en la matriz definida.
- Nombre el parámetro en la función después del archivo JavaScript.
El uso del nombre del archivo, por ejemplo, para foo.js nombra la variable ''foo'', aumenta la legibilidad del código, ya que la mayoría de los desarrolladores definirán ''foo'' como el parámetro para la dependencia foo.js. Al escanear el código o usar grep, es fácil encontrar todas las referencias al uso ''foo'' tanto dentro como fuera del módulo y facilita la selección de lo que el módulo está exponiendo al público. Por ejemplo, cambiar el nombre de bar.setBarOnFoo
a bar.setFooBar
es mucho más sencillo si la declaración en el módulo bar.js refleja el uso en otros archivos. Una simple búsqueda y reemplazo de bar.setBarOnFoo a bar.setFooBar en todos los archivos logrará la tarea.
Simplemente proporcione una API para su singleton como lo haría.
Y asegúrese de que está cargado perezosamente. La forma más fácil es usar una biblioteca de abstracción como el subrayado que proporciona ayuda a los navegadores cruzados. Otras opciones son Object.defineProperty ES5 o getter / setters personalizados.
En este caso _.once
garantiza que el resultado del constructor se almacena en la _.once
caché después de la primera llamada, básicamente lo carga de forma perezosa.
define(function() {
var constructor = _.once(function() {
...
});
return {
doStuffWithSingleton: function() {
constructor().doStuff();
}
};
});
_.once
desde el guión bajo.
Usted haría que una variable de nivel de módulo. Por ejemplo,
// In foo.js
define(function () {
var theFoo = {};
return {
getTheFoo: function () { return theFoo; }
};
});
// In bar.js
define(["./foo"], function (foo) {
var theFoo = foo.getTheFoo(); // save in convenience variable
return {
setBarOnFoo: function () { theFoo.bar = "hello"; }
};
}
// In baz.js
define(["./foo"], function (foo) {
// Or use directly.
return {
setBazOnFoo: function () { foo.getTheFoo().baz = "goodbye"; }
};
}
// In any other file
define(["./foo", "./bar", "./baz"], function (foo, bar, baz) {
bar.setBarOnFoo();
baz.setBazOnFoo();
assert(foo.getTheFoo().bar === "hello");
assert(foo.getTheFoo().baz === "goodbye");
};
Yo estaba en este escenario:
Por diferentes motivos, necesitaba llamar a una función que estaba en un módulo requirejs, pero el clic que disparó esa llamada no era necesario.
La forma en que solucioné esto fue creando una modura requirejs que escribe sobre el objeto de la ventana.
define("one", [], function() {
window.popupManager = (function () {
console.log (''aca'');
var popUpManager = function () {
self = this;
self.CallMe = function ()
{
alert (''someone calls'');
};
};
return new popUpManager();
})();
});
require([''one'']);
window.popupManager.CallMe();
De esta manera, si cualquier fragmento de código que está fuera del espectro requerido (sé que no debería ser así) puede llamar a las funciones de este requiere que se escriba sobre el objeto de la ventana.
Realmente sé que esta no es una solución "elegante", pero puede ayudarlo en caso de una emergencia.