54 votes

En utilisant RequireJS, comment puis-je passer des objets globaux ou des singletons autour ?

Disons que j'écris du code au niveau de la page principale et que deux dépendances requièrent la même instance d'un objet et l'indiquent également comme dépendance. Quelle est la manière appropriée de procéder ?

En gros, ce que je veux faire, c'est dire : "Si cette dépendance n'est pas chargée... alors chargez-la. Sinon, utilisez la même instance qui était déjà chargée et passez-la simplement."

58voto

Domenic Points 40761

Vous en feriez une variable de niveau module. Par exemple,

// 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");
};

8voto

Raynos Points 82706

Fournissez simplement une API pour votre singleton comme vous le feriez.

Et assurez-vous qu'il est chargé paresseusement. Le moyen le plus simple est d'utiliser une bibliothèque d'abstraction comme underscore qui fournit des aides pour tous les navigateurs. D'autres options sont ES5 Object.defineProperty ou des getter/setters personnalisés.

Dans ce cas _.once assure que le résultat du constructeur est mis en cache après le premier appel, il le charge paresseusement.

define(function() {
    var constructor = _.once(function() { 
        ...
    });

    return {
        doStuffWithSingleton: function() {
            constructor().doStuff();
        }
    };

});

_.once de l'underscore.

6voto

Domenic Points 40761

Si l'on combine les préoccupations de Raynos concernant l'encapsulation avec la clarification de l'OP selon laquelle il veut exposer quelques méthodes sur un service de messagerie, je pense que c'est la bonne façon de procéder :

// 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 ne sera pas présent dans tous les navigateurs, il faudra donc inclure un polyfill tel que celui que Mozilla fournit .

Une autre approche (et à mon avis probablement meilleure) serait de faire de l'objet du service de messagerie lui-même un module. Cela ressemblerait à quelque chose comme

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

Puisque vous exposez le même notify et listen à tous ceux qui utilisent votre module, et ceux-ci font toujours référence à la même privé listenerMap variable, cela devrait faire ce que vous voulez. Cela évite également d'avoir à utiliser Function.prototype.bind et se débarrasse de la distinction plutôt inutile entre le service de messagerie lui-même et le module qui en impose l'utilisation singleton.

1voto

Plantface Points 863

Voici une version où le module lui-même est la variable partagée au lieu d'une variable dans ce module.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X