Background
I decided I would practice by making a simple calculator app in JS. The first step was to implement a stack class. I ran into some problems however in achieving data encapsulation with the revealing prototype pattern (?). Here's how it looks right now:
Stack "class":
var Stack = (function () {
var Stack = function() {
this.arr = []; // accessible to prototype methods but also to public
};
Stack.prototype = Object.prototype; // inherits from Object
Stack.prototype.push = function(x) {
this.arr.push(x);
};
Stack.prototype.pop = function() {
return this.arr.length ? (this.arr.splice(this.arr.length - 1, 1))[0] : null;
};
Stack.prototype.size = function() {
return this.arr.length;
};
Stack.prototype.empty = function() {
return this.arr.length === 0;
};
return Stack;
})();
Test code:
var s1 = new Stack();
var s2 = new Stack();
for(var j = 1, k = 2; j < 10, k < 11; j++, k++) {
s1.push(3*j);
s2.push(4*k);
}
console.log("s1:");
while(!s1.empty()) console.log(s1.pop());
console.log("s2:");
while(!s2.empty()) console.log(s2.pop());
The Problem
The only problem is that the arr
is accessible. I would like to hide the arr
variable somehow.
Attempts at a Solution
My first idea was to make it a private variable like Stack
:
var Stack = (function () {
var arr = []; // private, but shared by all instances
var Stack = function() { };
Stack.prototype = Object.prototype;
Stack.prototype.push = function(x) {
arr.push(x);
};
// etc.
})();
But of course this approach doesn't work, because then the arr
variable is shared by every instance. So it's a good way of making a private class variable, but not a private instance variable.
The second way I thought of (which is really crazy and definitely not good for readability) is to use a random number to restrict access to the array variable, almost like a password:
var Stack = (function() {
var pass = String(Math.floor(Math.pow(10, 15 * Math.random()));
var arrKey = "arr" + pass;
var Stack = function() {
this[arrKey] = []; // private instance and accessible to prototypes, but too dirty
};
Stack.prototype = Object.prototype;
Stack.prototype.push = function(x) {
this[arrKey].push(x);
};
// etc.
})();
This solution is... amusing. But obviously not what I want to do.
The last idea, which is what Crockford does, allows me to create a private instance member, but there's no way I can tell to make this visible to the public prototype methods I'm defining.
var Stack = (function() {
var Stack = function() {
var arr = []; // private instance member but not accessible to public methods
this.push = function(x) { arr.push(x); }; // see note [1]
}
})();
[1] This is almost there, but I don't want to have the function definitions within the var Stack = function() {...}
because then they get recreated every time that an instance is created. A smart JS compiler will realize that they don't depend on any conditionals and cache the function code rather than recreating this.push
over and over, but I'd rather not depend on speculative caching if I can avoid it.
The Question
Is there a way to create a private instance member which is accessible to the prototype methods? By somehow utilizing the 'bubble of influence' created by the enclosing anonymous function?