1

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?

0

5 Answers 5

3

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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

2 Comments

+1 Another approach is to use Crockford's private pattern, but every instance gets its own print function. Or you can use a private, random property name which is common to the entire class, which avoids that problem. Code outside the class can't rely on the name. This is very similar to how the ES6 "private name" objects will work. Details (disclaimer: it's my blog, but it's not monetized): blog.niftysnippets.org/2013/05/…
OP said that he needs some private members, is myVar private here?
1

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

1 Comment

+1, this is Crockford's private pattern. Truly private members, but of course, getMyVar is duplicated for all instances.
0

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

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.