Reputation: 1654
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:
Upvotes: 1
Views: 193
Reputation: 4339
I don't have an answer at your questions, but a suggestion that should solve all your problems in a simpler way:
function Person(name) {
"use strict";
if (!this) return new Person(name);
function getName() {
return name;
}
this.who = getName;
}
Person.prototype.what = function() {
return this.constructor.name + ": " + this.who();
}
MDN documentation about function context
Alternative that works in all browsers
function Person(name) {
if (!(this instanceof Person)) return new Person(name);
function getName() {
return name;
}
this.who = getName;
}
Person.prototype.what = function() {
return this.constructor.name + ": " + this.who();
}
Upvotes: 1