Tek
Tek

Reputation: 3050

Closures and multiple instances

I like closures because you can make an API from them but it sucks that you can't have multiple instances.

http://jsfiddle.net/LxCJe/1/

var Person = (function () {

    // private properties and methods
    var Constr, name;

    // public API
    Constr = function (n) {
        name = n;
    };

    Constr.prototype.sayName = function(){return name;};

    return Constr;
}());


var person1 = new Person('Foo');
var person2 = new Person('Bar'); //New values will overwrite the name because of the closure.

console.log(person1.sayName()); //Bar
console.log(person2.sayName()); //Bar

Is there an alternative way to be able to use prototypes to access private members and create different instances?

Upvotes: 3

Views: 315

Answers (3)

thefourtheye
thefourtheye

Reputation: 239443

If you really want to make use of constructor and the members as private, then you can do like this

var Person = function(my_name) {

    // private properties and methods
    var Constr, name;

    // public API
    Constr = function(n) {
        name = n;
    };

    Constr.prototype.sayName = function() {
        return name;
    };

    return new Constr(my_name);
};

Note:

  1. But this is not efficient, as we have to create Constr constructor everytime you create an object of Person.

  2. It makes inheriting from Constr/Person impossible, as Constr cannot be accessed from outside and the prototype of Person is empty.

    console.log(Person.prototype); // {}
    

I would think that, not all the variables would be private in your class. So, you can have private members, like this

var Person = function(my_name) {

    // private properties and methods
    var name = my_name;

    // public API
    this.getName = function() {
        return name;
    }

    this.setName = function(newName) {
        name = newName;
    }
};

Person.prototype.printName = function() {
    console.log(this.getName());
}

var person1 = new Person('Foo');
var person2 = new Person('Bar');

console.log(person1.getName()); // Foo
console.log(person2.getName()); // Bar
console.log(Person.prototype);  // { printName: [Function] }
person1.printName();
person2.printName();

Upvotes: 2

Woahdae
Woahdae

Reputation: 5051

I think the way of the future is Object.defineProperties, which has compatibility with browsers that are generally HTML5 compatible (IE9+, most notably; see es5-shim for the best backfilling you can do).

With that, you could define read-only getters that look like attributes (not functions), and don't foul up prototypal inheritance or create a new constructor function every time: (JSFiddle here)

// name is read-only, but only modern browsers (ie 9+)
function Person(attributes) {
    Object.defineProperties(this, {
        name: {value: attributes.name}
    })
}

person1 = new Person({name: 'Foo'});
person2 = new Person({name: 'Bar'});

console.log(person1.name) // Foo
console.log(person2.name) // Bar

Alternately, similar to what someone else commented, you could do the following for better browser compatibility while maintaining prototypal correctness and a read-only API:

// read-only-ish, but better browser compatibility
function Person(attributes) {
    this._name = attributes.name
}
Person.prototype.name = function() { return this._name }

person1 = new Person({name: 'Foo'});
person2 = new Person({name: 'Bar'});

console.log(person1.name()) // Foo
console.log(person2.name()) // Bar

Upvotes: 1

Ja͢ck
Ja͢ck

Reputation: 173542

You can reduce the number of constructors here and create a new public interface each time new Person(name) is called:

function Person(name)
{
    // return public api that closes over 'name'
    return {
        sayName: function() {
            return name;
        }
    };
}

The public interface that you return will no longer be an instance of Person, that's something to keep in mind about this particular approach.

Upvotes: 0

Related Questions