54

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."

egervari
  • 22,372
  • 32
  • 121
  • 175

6 Answers6

58

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");
};
Domenic
  • 110,262
  • 41
  • 219
  • 271
  • 1
    That's implementing globals without globals. Now you can write to the global `foo` object anywhere you want. – Raynos Apr 10 '11 at 00:00
  • 3
    @Raynos: false; it's implementing the singleton pattern, which is object-oriented programming's method of solving the problems that global variables solve in procedural languages. – Domenic Apr 10 '11 at 00:02
  • 8
    Furthermore, it does not pollute the global namespace. It is only available to those modules that explicitly state `foo` as a dependency. – Domenic Apr 10 '11 at 00:02
  • @Domenic you can still alter the `theFoo` instance from anywhere. You can set baz in bar and set bar in baz. This feels bad to me. My issue is with return a reference from `getTheFoo` rather then a stateless interface – Raynos Apr 10 '11 at 00:06
  • 1
    @Raynos It sounds like you have an issue with RequireJS's module system and JavaScript's expando-property nature, then, rather then the particular solution I am giving here. The OP specifically requests `bar` and `baz` to both have access to "the same instance of an object." – Domenic Apr 10 '11 at 00:13
  • @Raynos just FYI, the pattern you seem to be hoping for is not possible in JavaScript unless the browser implements ES5's `Object.freeze`, which we cannot assume with the current state of the web (since IE less than 9 does not implement it). – Domenic Apr 10 '11 at 00:19
  • @Domenic I realised I'm being nitpicky with editing state through properties rather then methods. Your solution is reasonable, the example would just have read nicer with methods instead of properties. Actually I really like requireJS and JavaScript. I also don't see how Object.freeze is applicable. – Raynos Apr 10 '11 at 00:19
  • 1
    I think this is what I want. I don't want to manipulate the state of foo, but that's okay. I will play with this idea and let you know how it works. The reason I need this singleton is because I have a message service that has notify() and listen() methods... but two different dependencies need to access the same message service so they can talk to each other without having hard-wired dependencies. I will report back ;) – egervari Apr 10 '11 at 00:59
  • 1
    In that case I would use Raynos's solution, and only expose the `notify()` and `listen()` methods, while keeping the message service object itself private :). – Domenic Apr 10 '11 at 01:03
  • Well, I like your solution because I don't have to add any more libraries - that's a plus in many ways. There is state actually (my bad). When you listen to a message, it adds an object to the messaging service. When you notify, it checks the for all the things that are listening. So, this needs to be the same instance. If both calls happened on two differences instances, the notify and listen will be on two different objects, and it won't work. – egervari Apr 10 '11 at 01:43
  • @egervari: that wasn't quite what I meant; I'll post a separate answer explaining. – Domenic Apr 10 '11 at 03:16
  • Well, I did something similar that also worked. Actually, I was doing this a little before, but I'm glad to know that it's definitely work like it's supposed to. This works fine for this project. Thanks! – egervari Apr 10 '11 at 03:30
  • @egervari it's a pity you can't play with underscore, it's great for adding in high level functional style abstractions. – Raynos Apr 10 '11 at 13:11
  • @Domenic, why don't you just return `theFoo`? Why do we need an object that will return `theFoo` instead of returning `theFoo` directly? – Juliusz Gonera May 31 '11 at 08:12
  • 1
    @Juliusz Gonera: the same reason you don't expose public fields in Java or C#, but instead use getters: encapsulation. If I returned theFoo directly, some other module could do `foo.theFoo = "no longer an object suckas!"`, which would screw up everything. – Domenic May 31 '11 at 13:13
  • @Domenic, maybe I'm wrong but I think I recreated this situation at http://jsfiddle.net/7QkRK/ and I can't see the problem. Won't `foo.theFoo = "no longer an object suckas!"` just assign a new object to `foo.theFoo`, leaving the original one intact? – Juliusz Gonera Jun 01 '11 at 15:48
  • @Juliusz nope, the situation you've recreated is trying to reassign the module (which won't work, and will just make your `foo` variable point to a different memory location). The situation I am describing is reassigning a *property* of the module, which will work as it will make all future calls to `foo.theFoo` return the new value. Recreated here: http://jsfiddle.net/M48GU/1/ – Domenic Jun 01 '11 at 18:50
8

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.

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • @Domenic it will work if you use `_.once`. I actually recommend `_.once` or your own lazy loading getters if you cannot use ES5. – Raynos Apr 10 '11 at 00:21
  • I see what you mean. But you can still do `foo.lazyloaded.bar = "not me"` in the `baz` module; using a constructor function instead of `theFoo = {}` really doesn't solve any problems, although of course it is more powerful if the OP does indeed require an object constructed in that fashion. – Domenic Apr 10 '11 at 00:25
  • @Domenic I realised that was an issue. So I changed my answer to not actually give access to lazily loaded object since that is making it too public. – Raynos Apr 10 '11 at 00:29
  • excellent :). So now I think we have things covered... your answer gives a better best-practices solution, whereas mine covers the OP's specific (if perhaps misguided) request for "the same instance of an object." – Domenic Apr 10 '11 at 00:47
  • Seems excessive to use an entire library for the purposes of making a Singleton. The accepted answer isn't exactly a Singleton either - just a kind of variable factory. A straightforward JS Singleton example is here: http://www.dofactory.com/javascript/singleton-design-pattern – Hal50000 Dec 10 '15 at 20:32
6

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 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 private 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.

Domenic
  • 110,262
  • 41
  • 219
  • 271
  • Any reason to prefer `obj.f.bind(obj)` vs `function() { obj.f(); }`. Also an abstraction like {`_.bindAll(obj)`](http://documentcloud.github.com/underscore/#bindAll) is great for these cases. – Raynos Apr 10 '11 at 13:08
  • @Raynos: no great reason, except that if you are using a native version of it then the intermediate function won't show up in the stack trace while debugging. And I guess it will probably be slightly faster, but of course that's just micro-optimization. – Domenic Apr 10 '11 at 18:09
  • In messagingServiceSingleton.js you have ‘define(function () {’ but {where is the MessagingService it references?} as http//google.com/search?q=MessagingService+Javascript finds none, so shouldn’t that define begin ‘define(["./messagingService"], function (MessagingService) {’ to reference your custom messagingService.js? If correction needed, pls do. – Destiny Architect Jun 01 '16 at 19:36
1

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
Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
0

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.

Eric Bronnimann
  • 941
  • 7
  • 8
  • 1
    If you use the `exports` module, you should really give it the name `exports` in the parameters of your anonymous function? Why? Convention. 99.998...% of the code out there that uses the `exports` mechanism follows the convention. Setting yourself against it makes your code harder to read. Actually, the `exports` module exists solely to support convention: namely, the CommonJS module idiom. There's no compelling reason to use `exports` unless you want to use this idiom, which you do not use in your example code. – Louis Feb 03 '16 at 08:34
  • About your ‘Declare 'exports' as the first dependency in the define array. [then] Name the parameter in the function after the JavaScript file’: (1) for ‘file’ don't mean ‘module’ as latter can be bundled, (2) not expecting nor seeing that this messed me up, too, in reading ur code now, so I +1 @Louis cmt, including as (3) if breaking convention anywhere, (smart & appreciated) convention is to, near ideally {right-before --not yet done here}, give alert of breaking as ‘ALERT: I name "exports" use by the module name’ ideally with link to full explanation, (4) ...(my next comment) – Destiny Architect Jun 01 '16 at 20:34
  • -(my prev cmt) (4) I read ur rebuttal: IMHO cute idea but I still not buying as (a) replicates {something: file name} that readily changes, (b) ur global renaming trick cute BUT {that generally a big op so not costly to also search-in-replace in module def} and {module use sometimes NOT match its file name,for GOOD reason} and {handling these woes&more,I invent&do: every non-local name has suffix of ‘_’+{a globally unique to it permanent short suffix, specifically KCGUID}, and auto-complete makes typing easy, so no collisions & esp auto-find-all-uses even after incomplete renames of all uses}. – Destiny Architect Jun 01 '16 at 20:55
-1

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.

Sanchitos
  • 8,423
  • 6
  • 52
  • 52