user595419
user595419

Reputation:

How to correctly use the module pattern in JavaScript?

In my company we have a survey framework to help stakeholders create surveys, I'm trying to create a re-usable object which will allow a team member to easily set the width of a particular question for a survey - they can sometimes be a bit squished depending on the length of the answers. I'm trying to use a combination of the module and constructor pattern but not sure if I pulled it off correctly. Is there a better way to write my code?

        var WidthIncreaser = (function(){
            return function (element, words, width) {

                var element = $(element);
                var re = new RegExp(words, 'gi');

                return {
                    init: function() {
                        if (element.text().match(re)) {
                            element.width(width);
                        }
                    }
                };
            };
        })();

        var tr = new WidthIncreaser('td.choicev-question:first', 'Applicable from the', 400);
        tr.init();

The idea is, somebody can create a new instance of WidthIncreaser and pass in an element, a string which matches the question's text so it's the right question being targeted and the size to set width of the question to.

Thanks for the advice in advance!

Upvotes: 2

Views: 434

Answers (3)

ZER0
ZER0

Reputation: 25322

I don't think you actually need "the module pattern" here. You can just take advantages of closures and that's all:

function WidthIncreaser(element, words, width) {
    element = $(element);
    var re = new RegExp(words, 'gi');

    this.init = function () {
        if (element.text().match(re)) {
            element.width(width);
        }
    }
}

var tr = new WidthIncreaser('td.choicev-question:first', 'Applicable from the', 400);

tr.init();

Of course you don't need necessary init in that case, because you could put everything in the ctor, but I assume it's just an example and maybe you need lazy initialization.

In that way you can keep your prototype chains, and you statements like:

tr instanceof WidthIncreaser // true

will works.

In addition, you can also populate the prototype with methods that doesn't need to access to the scoped variables, at least not directly:

WidthIncreaser.prototype.doSomething = function() { /* .. */ }

For instance, if you can't use getter and setter because cross browsing restriction, you could have a function like that:

function WidthIncreaser(element, words, width) {
    element = $(element);
    var re = new RegExp(words, 'gi');

    this.element = function() { return element };

    this.init = function () {
        if (element.text().match(re)) {
            element.width(width);
        }
    }
}

WidthIncreaser.prototype.reset = function () {
    this.element().text("")
}

So basically you can retrieve the element from outside, but it's read-only, the WidthIncreaser can set elements only during the instantiation.

Edit: I copied and pasted init that of course it didn't work for a dependency with re, so it was a bad example to illustrate the approach.

Upvotes: 0

jbabey
jbabey

Reputation: 46647

The module pattern I use usually looks like this:

var myModule = (function () {
    var myPrivateVar = 'foo';
    var myPrivateFunc = function () {
        console.log('bar');
    };

    return {
        myPublicFunc: function () {
            console.log(myPrivateVar);
        }
    };
}) ();

Then it would be used like this:

myModule.myPublicFunc();

Upvotes: 0

Joseph
Joseph

Reputation: 119837

You are double wrapping stuff. Anyways, the common module pattern I see is just a function that returns an object with closure.

No need for the new keyword nor an immediate function. Immediate functions are usually used when only one object is made and assigned directly to one variable. In your case, you wanted to make "instances".

var WidthIncreaser = function(element, words, width) {

    var element = $(element),
        re = new RegExp(words, 'gi');

    return {
        init: function() {
            if (element.text().match(re)) {
                element.width(width);
            }
        }
    };
};

var tr = WidthIncreaser('td.choicev-question:first', 'Applicable from the', 400);

tr.init();​

Upvotes: 1

Related Questions