Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton pattern vs. Global variable use cases in Javascript?

Background

I'm teaching myself programming (focused on JS at the moment) and having trouble conceptualizing Singleton design patterns. The (https://en.wikipedia.org/wiki/Singleton_pattern) introduction states:

"[S]ome who are critical of the singleton pattern [consider it to introduce] global state into an application." (1st paragraph)."

The body of the article states: "Singletons are often preferred to global variables because: They do not pollute the global namespace (or, in languages with namespaces, their containing namespace) with unnecessary variables."

Considered together, these statements muddle my understanding of what singletons are and when they should be used in place of global variables.

The Question/Ask

Could anyone share an example scenario that could be implemented in JS with either a Singleton or Global Variable and provide a use case for when each strategy would be appropriate? Analogies and metaphors alongside code are extra helpful!

like image 530
samBuchl Avatar asked May 13 '16 13:05

samBuchl


1 Answers

Namespace

Consider an index.html like this:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Namespace</title>
</head>

<body>
    <div id="output">&nbsp;</div>

    <script src="scriptONE.js"></script>
    <script src="scriptTWO.js"></script>

    <script>
        print();
    </script>
</body>

</html>

Where scriptONE.js holds

var output = "ONE";

var print = function() {
    var div = document.getElementById("output");
    div.innerHTML = output;
}

And scriptTWO.js contains

var output = "TWO";

var print = function() {
    var div = document.getElementById("output");
    div.innerHTML = output;
}

... running this, what will happen? Right, the website is gonna display TWO. Because both scripts are basically polluting the global namespace and thus conflicts occur. ScriptONE's print is replaced by scriptTWO's function of the same name.

The problem remains if we edit scriptTWO.js like that

var output = "TWO";

var printTWO = function() {
    var div = document.getElementById("output");
    div.innerHTML = output;
}

... because? Right! output is still polloitering around @global. By the time print() is called var output already has been overwritten.

Now, instead of checking every script we scavenged somewhere (scriptONE) and avoid every variable name it puts out into the global namespace, we could contain our own script (scriptTWO).

// obviously our countainer should not be named "print"
var myPrintDemo = {};

(function PRINTMODULE(api) {
    var output = "TWO";

    api.print = function () {
        var div = document.getElementById("output");
        div.innerHTML = output;
    }
}(myPrintDemo));

Our index.html now displays: ONE and we could replace print() with our new myPrintDemo.print() to make the page display TWO.

...but why? What happened? What's that voodoo that we did there?

We created the object myPrintDemo. Next we 'immediately invoked' the function PRINTMODULE. This is done whenever you encounter something like this (function () { ... }());. The function is called immediately; hence the pattern's name: Immediately-invoked function expression.

We used this pattern and fed it our object (by putting it into the last pair of ()). We told our function to internally reference to it as api. And now everything without api. in front of it is basically a private variable, that's only visible within the scope of our PRINTMODULE function. Yet everything we "prefix" with api. is going to be accessible as one of object myPrintDemo's attributes.

This module pattern is that handy(, voodoo) and important, you should definitely check it out more & someplace more competent than me. Maybe a book? I enjoyed Douglas Crockford's "Javascript The Good Parts" a great deal. Heck, it made me understand scope! I actually bought it, yet it can be read here, too: http://bdcampbell.net/javascript/book/javascript_the_good_parts.pdf

Learning JScript you could rely even more on Crockford, using http://jslint.com/ .

Opinions may differ, but speaking for myself: nothing taught me more than hacking something together ...and then making it pass jslint!

Singleton?

All right. So the use of a single object to contain all our stuff has its merits. The point that it "introduces global state into an application" is obviously moot, because JScript comes with global.

But is this module design pattern truly a singleton? Nah. We try to contain ourselves to a single object in the global namespace. But a singleton actually needs the feature to be necessarily unique.

var Singleton = (function () {
    var instance;

    function createInstance() {
        var that = {};

        // define our module here

        return that;
    }

    // now this is the part that makes it a singleton:
    return {
        get: function () {
            if (!instance) {
                // only create an instance if there is none!
                instance = createInstance();
            }
            // always return the same object!
            return instance;
        }
    };
})();

As you notice: this is a slightly different way to fill our object with its content. Instead of first declaring Singleton an object and then feeding it to an iffy (Immediately-invoked function expression), we directly declare it to be the result of an iffy.

You can use either way. This difference is not what makes this pattern produce something more of a true Singleton. This is JScript's implementation of a Singleton, because of the check if there already is an instance of whatever goes on inside createInstance().

(I am using the first way for its obvious yet convenient use of api. to make things public).

like image 71
Jonas M. Schlatter Avatar answered Oct 25 '22 15:10

Jonas M. Schlatter