Javascript Module Pattern and the new keyword
Asked Answered
A

3

8

I get the basics of the Module Pattern and its use of a closure to allow for private members, BUT I can't quite swallow why the below code does what it does:

var Calculator = function() {
    var priv = 0;

    return {
        changePriv: function() { priv++;},
        printPriv: function() { console.log(priv);}
    }
}

var myCalc = Calculator();
myCalc.printPriv();
myCalc.changePriv();
myCalc.printPriv();

var myOtherCalc = Calculator();
myCalc.printPriv();

The console output is

0
1
1

So purposefully omitting the new keyword here, the first call sets myCalc to a Calculator object. It starts out with a priv value of 0, has that incremented, and then prints out its new priv value of 1.

But a) WHY does the next call to Calculator() end up returning a reference to the SAME object (as evidenced by the second '1')? I know I can use new here and avoid that, but don't get why I have to. Isn't this function using object literal syntax to essentially create a new object and then return it? b) Since it does seem to be using the same function stack space (is that even the right way to think of it in JS?), why doesn't it zero out the priv variable in the process before returning the reference to the same object?

EDIT: Corrected sloppy/stupid mistake (thanks scessor), which DOES now output a new/distinct calculator object even without use of the new keyword. So that clears up a) and b). My resulting question would have been "Does it matter whether I use new or not in the invocation of a module-pattern constructor. The answer is, I guess it doesn't matter(?). (Joseph: see http://jsfiddle.net/MvMvy/5/ ...the instanceof operator simply doesn't work with the module pattern either way.)

Amerce answered 18/4, 2012 at 5:51 Comment(2)
By convention, functions starting with a capital letter are reserved for constructors (i.e. functions that are supposed to be called with new). The Calculator function explicitly returns an object so calling it with new doesn't make any difference to what it returns.Suctorial
Thanks RobG. That's exactly what I was hoping to get confirmation on.Amerce
Y
9

You don't output the other calculator myOtherCalc: if you want to compare them, replace the third myCalc.printPriv(); with:

myOtherCalc.printPriv();

Then the output is:

0
1
0
Yate answered 18/4, 2012 at 5:54 Comment(0)
C
3

You don't need to use new in your case.

Normally, if you use new, you expect that what you get is an instance of that constructor you called. In your case, it won't because you manually returned an object. It would make no sense and will cause problems later of you confuse the usage. Soon you might be "instance testing" your objects, and will run into this "mismatch".

and you got a typo in your code:

var myCalc = Calculator();       //create calculator
myCalc.printPriv();              //"myCalc" private is 0
myCalc.changePriv();             //increment
myCalc.printPriv();              //"myCalc" private is 1

var myOtherCalc = Calculator();  //another calculator
myCalc.printPriv();              ///but you printed "myCalc" again
Celinacelinda answered 18/4, 2012 at 6:27 Comment(1)
"must not" and "should not" are different statements. I would say in his specific example he should not, however... There are versions of the module pattern which play well with "new" and do not encounter the instanceof problem you describe. See: carldanley.com/js-module-patternAnishaaniso
C
0

There's nothing to do with the "new" Operator... Here you get a well explained topic about proto/constructor : http://en.wikibooks.org/wiki/JavaScript/Access_Control

However it's a nonsense example, you can do this, so you can access priv via getter and setter methods only :

function Calculator2() {
var priv = 0;
this.public = 0;
this.getPriv = function(){
    return  priv;
}
this.setPriv = function(val){
    priv = val;
}
}
Calculator2.prototype.changePriv = function(){
this.setPriv(this.getPriv()+1);
}
Calculator2.prototype.printPriv = function(){
    console.log("priv = " + this.getPriv());
}
Calculator2.prototype.changePublic = function(){
    this.public++;
}
Calculator2.prototype.printPublic = function(){
    console.log(this.public);
}

In this case var priv is always accessible via a getter and a setter method,

In the next example you have a private var className and another public var __className :

<div id = "outputDiv" style="width:600px;height:400px;border:solid 1px #000"></div>
<script type="text/javascript">
    //<![CDATA[

//script : var SomeClass = function(className) {

    var __className__ = className;
    this.__className__ = "\"public default className\"";
    var someString = new String("");

    this.setScopeText = function() { // void
        someString = "A new instance of \"private [__classname__] : " +
        __className__ + "\"" +
        " has been created. Refering to [__className__]<br />" +
        "A new instance of " +
        this.__className__ +
        " has been created. Refering to [this.__className__]";
        return someString;
    };

    this.getScopeText= function (){
        return someString;
    }

    this.setOutput = function(elementId, someString){
        var outputPane = this.getSomePane(elementId);
        outputPane.innerHTML += "<p>" + someString + "</p>";
    }

    this.getSomePane = function(elementId){
        var outputP = document.getElementById(elementId);
        return outputP;
    }

}

SomeClass.prototype.changeClassNameVariable = function( str ){
    this.__className__  = str;
}

// end declaration.

//tests :

var sc = new SomeClass("foo");

sc.setOutput("outputDiv",sc.__className__);
sc.setOutput("outputDiv",sc.setScopeText());
sc.setOutput("outputDiv",sc.getSomePane("outputDiv"));

sc.__className__ = "\"Some name\"";
sc.setOutput("outputDiv",sc.__className__);
sc.setOutput("outputDiv",sc.setScopeText());

sc.changeClassNameVariable("bar");
sc.setOutput("outputDiv",sc.__className__);
sc.setOutput("outputDiv",sc.setScopeText());

// ends javascript and CDATA section

//]]>
</script>

Output in "div:outputDiv" :

"public default className"

A new instance of "private [classname] : foo" has been created. Refering to [className] A new instance of "public default className" has been created. Refering to [this.className]

[object HTMLDivElement]

"Some name"

A new instance of "private [classname] : foo" has been created. Refering to [className] A new instance of "Some name" has been created. Refering to [this.className]

bar

A new instance of "private [classname] : foo" has been created. Refering to [className] A new instance of bar has been created. Refering to [this.className]

-> className declared in constructor never changes! -> this.className or SomeClass.prototype.className is public and may be changed.

I hope this may help to understand the chain and my comment more clearly...

Champollion answered 18/4, 2012 at 7:5 Comment(2)
Your example doesn't allow for private methods, which is the point of the module pattern. (getPriv is accessible anywhere - see jsfiddle.net/VjnGq/1 )Amerce
This is why I said "this is a nonsense example" It was only to show how it works... getPriv is accessible however priv is not if not calling getPriv() method...Champollion

© 2022 - 2024 — McMap. All rights reserved.