Using RequireJS, how do I pass in global objects or singletons around?

JavascriptSingletonRequirejs

Javascript Problem Overview


Let's say I am writing code at the main page level and 2 dependencies require the same instance of an object and also state that as a dependency. What is the appropriate way to go about this?

Basically what I want to do is say, "If this dependency isn't loaded... then load it. Otherwise, use the same instance that was already loaded and just pass that one."

Javascript Solutions


Solution 1 - Javascript

You would make that a module-level variable. For example,

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

Solution 2 - Javascript

Just provide an API for your singleton as you would.

And make sure its lazily loaded. The easiest way is to use an abstraction library like underscore that supplies cross browser helpers. Other options are ES5 Object.defineProperty or custom getter/setters.

In this case _.once ensures that constructor's result is cached after the first call, it basically lazy loads it.

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

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

});

_.once from underscore.

Solution 3 - Javascript

Combining Raynos's concerns about encapsulation with the OP's clarification that he wants to expose a couple of methods on a messaging service, this is I think the right way to go about it:

// 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 will not be present in all browsers, so you would need to include a polyfill like https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind">the one Mozilla provides.

An alternate (and in my opinion probably better) approach would be to make the messaging service object itself a module. This would look something like

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

Since you expose the same notify and listen methods to everyone who uses your module, and those always refer to the same http://javascript.crockford.com/private.html">private</a> listenerMap variable, this should do what you want. It also obviates the need for Function.prototype.bind, and gets rid of the rather-unnecessary distinction between the messaging service itself and the module which enforces singleton usage of it.

Solution 4 - Javascript

Here's a version where the module itself is the shared variable instead of a variable within that 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

Solution 5 - Javascript

As a variation of Domenic's answer, you can use the 'exports' magic module to automatically generate a reference for the module -- "Properties added to the exports object will be on the public interface of the module, no need to return any value." This avoids having to call the getTheFoo() function to obtain a reference.

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

To address the comment below, I personally have found the above convention to be useful. Your mileage may vary, but feel free to adopt the convention if you think it is useful. The convention boils down to these two rules:

  • Declare 'exports' as the first dependency in the define array.
  • Name the parameter in the function after the JavaScript file.

Using the name of file, e.g. for foo.js name the variable 'foo', increases the readability of the code as most developers will define 'foo' as the parameter for the foo.js dependency. When scanning the code or using grep, it is easy to find all references to 'foo' use both inside and outside the module and it makes it easy to pick out what the module is exposing to the public. For example, renaming bar.setBarOnFoo to bar.setFooBar is much easier if the declaration in the bar.js module mirrors the usage in other files. A simple search and replace of bar.setBarOnFoo to bar.setFooBar across all files will accomplish the task.

Solution 6 - Javascript

I was in this scenario:

For different reasons I needed to call a function that was on a requirejs module, but the click that fired that call was out of require.

The way I fixed this was creating a requirejs modure that writes over the window object.

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();

This way if any piece of code that is out of the require spectrum (I know it shouldn't be this way) can call functions of this require that writes over the window object.

I really know this is not an "elegant" solution, but it may help you in case of an emergency.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionegervariView Question on Stackoverflow
Solution 1 - JavascriptDomenicView Answer on Stackoverflow
Solution 2 - JavascriptRaynosView Answer on Stackoverflow
Solution 3 - JavascriptDomenicView Answer on Stackoverflow
Solution 4 - JavascriptBenny BottemaView Answer on Stackoverflow
Solution 5 - JavascriptEric BronnimannView Answer on Stackoverflow
Solution 6 - JavascriptSanchitosView Answer on Stackoverflow