I'm asking this because I think I found a solution to a JavaScript problem, and I'd like to know if I'm way off base. I hope that asking this here is appropriate.
As I'm sure you know, JavaScript does not have private properties. This issue is usually solved using one of two patterns.
The first is to create privileged methods inside the constructor, using closures, and bind them to the object using the this
keyword. This is the technique used by Douglas Crockford. Here's an example:
function Person(name) {
function getName() {
return name;
}
this.who = getName;
}
The problem with this pattern is that there's a big danger of polluting the global namespace:
var me = Person("Karl"); // Oops! no "new"
me.who(); // TypeError: Cannot read property 'who' of undefined
who(); // "Karl" - "who" is in global namespace!
The second solution is to use the module pattern. Instead of binding the privileged method to this
, you bind it to a new object:
function Person(name) {
function getName() {
return name;
}
return {
who: getName
}
}
var me = Person("Karl"); // No "new," but that's OK
me.who(); // "Karl"
who(); // TypeError: undefined is not a function
The problem with that pattern is that the object does not have Person's prototype chain. So, you can't properly check its type, and you can't extend the object using the constructor's prototype:
console.log(me instanceof Person); // "false"
Person.prototype.what = function() {
return this.constructor.name + ": " + this.who();
}
me.what(); // TypeError: undefined is not a function
I've found a solution to this. Use a temporary constructor that has Person's prototype chain, and return an object constructed with the temporary constructor:
function Person(name) {
function getName() {
return name;
}
// Temporary constructor
function F() {
this.who = getName;
}
F.prototype = Person.prototype;
return new F();
}
// Can't pollute the global namespace
var me = Person("Karl");
me.who(); // "Karl"
who(); // TypeError: undefined is not a function
// Proper prototype chain: correct type checking, extensible
console.log(me instanceof Person); // "true"
Person.prototype.what = function() {
return this.constructor.name + ": " + this.who();
}
me.what(); // "Person: Karl"
I have a couple of related questions about this solution:
- Are there any drawbacks to this technique?
- Though the module pattern and the temporary constructor pattern have been around for a while now, I've never seen anyone put them together. Is this a pattern that I just didn't know about before?
- If the answer to number 2 is "no," can I get mad props for this? :)