Is there any way to make “private” variables (those defined in the constructor), available to prototype-defined methods?
TestClass = function(){
var priv
In current JavaScript, I'm fairly certain that there is one and only one way to have private state, accessible from prototype functions, without adding anything public to this
. The answer is to use the "weak map" pattern.
To sum it up: The Person
class has a single weak map, where the keys are the instances of Person, and the values are plain objects that are used for private storage.
Here is a fully functional example: (play at http://jsfiddle.net/ScottRippey/BLNVr/)
var Person = (function() {
var _ = weakMap();
// Now, _(this) returns an object, used for private storage.
var Person = function(first, last) {
// Assign private storage:
_(this).firstName = first;
_(this).lastName = last;
}
Person.prototype = {
fullName: function() {
// Retrieve private storage:
return _(this).firstName + _(this).lastName;
},
firstName: function() {
return _(this).firstName;
},
destroy: function() {
// Free up the private storage:
_(this, true);
}
};
return Person;
})();
function weakMap() {
var instances=[], values=[];
return function(instance, destroy) {
var index = instances.indexOf(instance);
if (destroy) {
// Delete the private state:
instances.splice(index, 1);
return values.splice(index, 1)[0];
} else if (index === -1) {
// Create the private state:
instances.push(instance);
values.push({});
return values[values.length - 1];
} else {
// Return the private state:
return values[index];
}
};
}
Like I said, this is really the only way to achieve all 3 parts.
There are two caveats, however. First, this costs performance -- every time you access the private data, it's an O(n)
operation, where n
is the number of instances. So you won't want to do this if you have a large number of instances.
Second, when you're done with an instance, you must call destroy
; otherwise, the instance and the data will not be garbage collected, and you'll end up with a memory leak.
And that's why my original answer, "You shouldn't", is something I'd like to stick to.