Question

So this is an example of the famous JavaScript Module Pattern:

var Person = (function() {

    var _name; // so called 'private variable'

    function Person(name) {
        _name = name;
    }

    Person.prototype.kill = function() {
        console.log(_name + ' has been shot');
    };

    return Person;
})();

var paul = new Person('Paul');
paul.kill();

So far so good right? This logs 'Paul has been shot' to the console, which is what we want.

But.

Is _name really a private variable? I would define a private variable as a variable that belongs to an instance of an object, which is not accessible for the outside world. The last part works, I can not access _name from outside the closure.

But if I do this:

var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill();
bran.kill();

This will then log 'Bran has been shot', twice. No Paul there. So _name is actually shared with all instances of my Person object. That's what I would define as a 'static variable', although it's also not accessible from outside.

So is there any way to create a real private member variable with the module pattern? One that is not static.

Something that also happens a lot is defining this._name inside the constructor function, but that kills the private part, it's now accessible from outside:

function Person(name) {
    this._name = name;
}

var bran = new Person();
console.log(bran._name); // yep, accessible

Question:

So. Private is not really private, just static. How do we create a real private member variable with the module pattern? A variable which belongs to an instance, which is not static, and a variable which is not accessible from the outside.

Was it helpful?

Solution

You're right; _name is more of a static variable. It is kept in the closure that contains the constructor, so every use of the constructor will use that same variable. And remember, this has nothing to do with classes, and everything to do with closures and functions. It can be pretty handy, but it isn't how you do private members.

Unsurprisingly, Douglas Crockford has a page devoted to private members in Javascript.

In order to make private members, you have to go 'one level deeper'. Don't use a closure outside of the constructor; use the constructor as the closure. Any variables or methods defined inside the constructor are obviously not usable by the outside world. In fact, they aren't accessible by the object, either, so they are rather extremely 'private'.

We want to use our private members though. :) So what to do?

Well, in the constructor, do this:

var Klass = function () {
    var private = 3;
    this.privileged = function () { return private; };
};

and then:

var k = Klass();
console.log(k.privileged()); // 3

See how that's using the constructor as a closure? this.privileged lives on, attached to the object, and thus private lives on, inside this.privileged's closure.

Unfortunately, there's one problem with private and privileged methods in Javascript. They must be instantiated from scratch each time. There is no code sharing. That's obviously what we want with private members, but it isn't ideal for methods. Using them slows down object instantiation and uses more memory. It's something to keep in mind when/if you run into efficiency problems.

OTHER TIPS

"Real private member variables" and prototype based methods do not play nice together. The only way achieve what you want is to create all methods in the constructor.

var Person = (function() {

    function Person(name) {
        this.kill = function() {
            console.log(name + ' has been shot');
        };
    }

    return Person;
})();

var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill(); // Paul has been shot
bran.kill(); // Bran has been shot

But this will use more memory and be slower since each instance has a unique version of the kill function.

Conventionally, the underscore prefix is used for semi-private instance properties, as long as the data exposed is not a security risk. Most consumers of your javascript code know not to mess with underscore prefixed properties.

Some more reading you may find useful is here.

The problem is that your _name variable is outside the Person scope and shared between all Person instances. Do something like the following instead :

(function() {

    var Person = function(name) {
         var _name = name; // so called 'private variable'

        this.getName = function()
        {
            return _name;
        }

        this.kill = function()
        {
            console.log(this.getName() + ' has been shot');
        }
    }

    /*Alternative
    Person.prototype.kill = function() {
        console.log(this.getName() + ' has been shot');
    };*/

    window.Person = Person;
})();

var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill();
bran.kill();

A variable that needs to be private in instance scope has to be in such scope, for example:

function Person(name) {
    var _name = name;

    this.kill = function() {
      console.log( _name + ' has been shot' ); 
    }
}

A drawback of this is that kill has to be defined in a scope that also has access to the private variable.

In order to create a private var, you should put it inside the constructor, so your code would be like this:

var Person = (function() {

    function Person(name) {
        // since name is a param, you don't even have to create a _name var
        // and assign name to it
        this.getName = function () {
            return name;
        };
    }

    Person.prototype.kill = function() {
        console.log(this.getName() + ' has been shot');
    };

    return Person;
})();

var paul = new Person('Paul');
paul.kill();

You can also declare .kill inside the constructor:

var Person = (function() {

    function Person(name) {
        this.kill = function() {
            console.log(name + ' has been shot');
        };
    }

    return Person;
})();

var paul = new Person('Paul');
paul.kill();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top