Bill
Bill

Reputation: 83

JavaScript Create class using module pattern

I create a Class using module pattern cuz I need some private members and functions. The problem is I can't create multiple instance for one module. If I create a new instance, it will replace all the instances I created before.

Here is the code.

var MyObj = (function() {
    var myVar;
    function MyObj(arg) {
       myVar = arg;
    }
    MyObj.prototype.print = function() {
       console.log(myVar);
    };
    return MyObj;
})();
var instance1 = new MyObj('instance1');
var instance2 = new MyObj('instance2');
instance1.print(); // instance2
instance2.print(); // instance2

Here is my questions: 1. Does this mean i can't create multiple instance for one Class if i want to use this pattern? 2. If i can't use this pattern, is there anyway else can have private in Class?

Upvotes: 1

Views: 1081

Answers (5)

gillup
gillup

Reputation: 38

I don't know much about prototyping, but I think the use of the module pattern would result in something simpler, like this :

var MyObj = function(arg){
    var myVar = arg;

    var pub = {
        print: function(){
            console.log(myVar);
        }
    }

    return pub;
};

var instance1 = MyObj('instance1');
var instance2 = MyObj('instance2');
instance1.print(); // instance1
instance2.print(); // instance2

Agreed, you don't have a 'Class' with this. But for me, the whole point of the module pattern is to have encapsulation without class-like hassles.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074228

You have at least four options:

  1. The solution grape_mao outlined, which is Crockford's private pattern. The problem with it is that every instance gets its own getMyVar function, they can't share them. This is the only way to get truly private properties in JavaScript right now.

  2. The solution Tibos outlined, where myVar isn't private anymore, but all of the prototype functions have access to it, you don't have new accessor functions for every instance.

  3. That same solution as #2, prefixing the property name with a _. Purely as a matter of convention, properties with names starting with _ are supposed to be off-limits to code outside the object. It's not remotely private, but it's cheap. I used to totally scoff at this approach, but someone pointed out to me that now that most modern languages have reflection, private members aren't really private even in Java and C# and such. :-)

  4. Use the pattern that ES6 will use with private Name objects, replacing the private Name objects (since they don't exist yet!) with random strings. Your code doesn't worry about the actual name of the property, and other code can't reasonably guess it. Still only pseudo-private, but much more private than #2 or #3; not as much as #1. I outline how this works in this article. Basically it looks like this:

    var MyObj = (function() {
        var myVarName = new Name();
        function MyObj(arg) {
           this[myVarName] = arg;
        }
        MyObj.prototype.print = function() {
           console.log(this[myVarName]);
        };
        return MyObj;
    })();
    var instance1 = new MyObj('instance1');
    var instance2 = new MyObj('instance2');
    instance1.print(); // instance2
    instance2.print(); // instance2
    

    ...where Name looks something like this:

    var Name = function() {
        var used = {};
    
        function Name() {
            var length, str;
    
            do {
                length = 5 + Math.floor(Math.random() * 10);
                str = "_";
                while (length--) {
                    str += String.fromCharCode(32 + Math.floor(95 * Math.random()));
                }
            }
            while (used[str]);
            used[str] = true;
            return new String(str); // Since this is called via `new`, we have to return an object to override the default
        }
    
        return Name;
    }();
    

    Details in the article.

Upvotes: 3

Tibos
Tibos

Reputation: 27823

You have multiple instances of the MyObj class, but you use myVar as a static variable, so it is shared between instances.

This code should work properly:

var MyObj = (function() {
    function MyObj(arg) {
       this.myVar = arg;
    }
    MyObj.prototype.print = function() {
       console.log(this.myVar);
    };
    return MyObj;
})();
var instance1 = new MyObj('instance1');
var instance2 = new MyObj('instance2');
instance1.print(); // instance1
instance2.print(); // instance2

Demo: http://jsbin.com/EPirAki/1/edit

Take a look at this blog post (curtesy of T.J. Crowder - see comments) that explains how you can use random to get near-private variables: http://blog.niftysnippets.org/2013/05/private-properties-in-es6-and-es3-and.html

Upvotes: 2

cbayram
cbayram

Reputation: 2259

Prototype functions can't directly access private variables.

var MyObj = (function() {
  var abc = "shared by all MyObj";   // this variable is shared through all  MyObj instances created and doesn't belong to MyObj
  function MyObj(arg) {
    var privateVar = arg;
    this.getPrivateVar = function(){ return privateVar; }
    this.myVar = arg;
  }
  MyObj.prototype.print = function() {
    //console.log(privateVar);  // cannot access variable
    console.log(this.getPrivateVar());  // using a getter function you can access that   private variable and limit its mutation
    console.log(this.myVar);
  };
  return MyObj;
})();
var instance1 = new MyObj('instance1');
var instance2 = new MyObj('instance2');
instance1.print(); // instance2
instance2.print(); // instance2

Upvotes: 0

grape_mao
grape_mao

Reputation: 1153

You'll need a privilege function who has access to the variable.

var MyObj = (function() {
function MyObj(arg) {
    this.getMyVar = function(){
         return arg;   
    };
}
MyObj.prototype.print = function() {
    console.log(this.getMyVar());
};
    return MyObj;
})();
var instance1 = new MyObj('instance1');
var instance2 = new MyObj('instance2');
instance1.print(); // instance2
instance2.print(); // instance2

Upvotes: 1

Related Questions