Reputation: 3050
I like closures because you can make an API from them but it sucks that you can't have multiple instances.
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
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:
But this is not efficient, as we have to create Constr
constructor everytime you create an object of Person
.
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
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
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