最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

closures - Javascript Module Pattern and the new keyword - Stack Overflow

programmeradmin3浏览0评论

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 / ...the instanceof operator simply doesn't work with the module pattern either way.)

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/MvMvy/5/ ...the instanceof operator simply doesn't work with the module pattern either way.)

Share Improve this question edited Apr 18, 2012 at 19:20 asked Apr 18, 2012 at 5:51 user718147user718147 2
  • 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. – RobG Commented Apr 18, 2012 at 6:01
  • Thanks RobG. That's exactly what I was hoping to get confirmation on. – user718147 Commented Apr 18, 2012 at 19:33
Add a ment  | 

3 Answers 3

Reset to default 9

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

myOtherCalc.printPriv();

Then the output is:

0
1
0

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

There's nothing to do with the "new" Operator... Here you get a well explained topic about proto/constructor : http://en.wikibooks/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 ment more clearly...

发布评论

评论列表(0)

  1. 暂无评论