Update:
This kind of implementation is simply bad, and I've removed that answer.
I just answered this question. The OP asked for the solution of a private member which can only be accessible by prototype methods. For my answer, I would not suggest to do that but propose the code of its possibility. (And sorry, I do not have a good idea with the title .. )
As we can see if any other prototype methods would access prop1
, will need to repeat this pattern. I've ever thought about to use a private array to achieve it, but this code seems significantly shorter.
But there are things not good:
- It needs an extra function to ensure
that
not reference to this
.
A.prototype.myFunc
is growing up(deeper) with the object creation afterwards.
- As every
var myFunc
are still referenced by A.prototype.myFunc
, there's a doubt even after invoked retire
and clean up all outter reference to an object, it might still alive when the gc comes.
- I have limited testing environment and be pleasure to know if there's potential risk with this implementation.
So I think an answer to the question could be:
A. A more feasible approach to alter the prototype methods in the constructor to achieve that the private members can only be accessible in prototype methods.
B. Another method to achieve the same thing, and the code is as simple as possible.
It would also be greatly appreciated to point out my misunderstanding of the closures and the garbage collection within your answers.
Let's see the requirements of the OP in the other question:
Is there a JavaScript pattern which mimics "Protected" object
properties
Answer: sort of, best way (in my opinion) name them _myPrivate
BTW - I do not want the pattern of privileged member functions
accessing private properties since the member function is still
public.
That just makes no sense at all, does the OP think that A.prototype.myFunc
is not publicly accessible on A instances?
An introduction to prototype and constructor functions (plus some patterns for privates) can be found here
1 . It needs an extra function to ensure that not reference to this.
There isn't a workaround. that
is captured by A.prototype.myFunc
within each instantiation, and the instance itself is the object which can access that
directly, more objects involve would just make things worse; the retire
method is already the simplest way to untangle the reference.
2 . A.prototype.myFunc is growing up(deeper) with the object creation afterwards.
This is just the potential risk. A.prototype.myFunc
is made similar to a recursive method, but in fact it isn't. It calls to the previous myFunc
and check the instance for its identity. For a few instances it isn't a problem, but for a plenty of the instances, the growing depth will finally cause stack overflow.
As the implementation will whatever need a mechanism for cleanning up, to make the calls deeper gains nothing than just use an array to hold the references, and to clean up on-demand.
3 . As every var myFunc are still referenced by A.prototype.myFunc, there's a doubt even after invoked retire and clean up all outter reference to an object, it might still alive when the gc comes.
The fact is var myFunc
which is captured by A.prototype.myFunc
will still alive even when the gc comes to collect garbages. There is almost impossible to make the reference to myFunc
be released, since it's a chained invocation, the contexts of a deeper call and the shallow call do not have the visibility to each other, thus none of them are able to modify the chain of invocation for skipping a level; unset myFunc
would just break the chain. Any trick trying to solve this would involve more objects, that may either increase the cost or being an overkill.
4 . I have limited testing environment and be pleasure to know if there's potential risk with this implementation.
As the answer to the bullet point 2, it may cause stack overflowing when a lot of object are created with it.
I tend to agree with people that say "just don't bother with private," but I think the best way to do this, if you really want it, is with Function#bind
. The Crockford article doesn't mention this approach, possibly because it predates bind
, and emulating bind
with apply
gets kind of hairy (or possibly because it's an extra bit of overhead for not much gain).
function classify(fn) {
var privateScope = {}, publicScope = {};
function bindProp(to, target, src, key) {
if (!src.hasOwnProperty(key)) return;
if (!(src[key] && src[key].bind)) return;
target[key] = src[key].bind(to);
}
function ctor() {
var instancePublic = {}, instancePrivate = Object.create(instancePublic);
for (var key in publicScope) {
bindProp(instancePrivate, instancePublic, publicScope, key);
}
for (var key in privateScope) {
instancePrivate[key] = privateScope[key];
}
if (publicScope.hasOwnProperty('constructor'))
publicScope.constructor.apply(instancePrivate, arguments);
return instancePublic;
}
fn.call(publicScope, publicScope, privateScope);
return ctor;
}
This function lets you define a pseudoclass with a "public" and "private" scope. The idea is that:
- The public scope object is placed in the prototype chain of the private scope object.
- All functions are bound to the private scope object.
First attempt
function classify(fn) {
var privateScope = {}, publicScope = {};
function bindProp(privateScope, scopeObject, key) {
if (!scopeObject.hasOwnProperty(key)) return true;
if (!(scopeObject[key] && scopeObject[key].bind)) return;
privateScope[key] = scopeObject[key].bind(privateScope);
}
function ctor() {
var instancePrivate = Object.create(privateScope),
instancePublic = Object.create(instancePrivate);
for (var key in publicScope) {
console.log(key);
bindProp(instancePrivate, publicScope, key);
}
for (var key in privateScope) {
if (!bindProp(instancePrivate, privateScope, key)
&& !publicScope.hasOwnProperty(key))
instancePublic[key] = void 0;
}
if (publicScope.hasOwnProperty('constructor'))
publicScope.constructor.apply(instancePrivate, arguments);
return instancePublic;
}
fn(publicScope, privateScope);
return ctor;
}
This version had the prototype chain reversed:
- The private scope object is placed in the prototype chain of the public scope object.
- All functions are bound to the private scope object.
- Any private member that's not shadowed by a public member is shadowed by
undefined
.
Usage
You'd use it something like this:
var Foo = classify(function(pub, priv) {
// constructors are supported but not required
pub.constructor = function(a, b) {
this.a = a;
this.b = b;
};
priv.somePrivateProp = "lol";
priv.doPrivateStuff = function(x, y) {
return x + y;
};
pub.somePublicProp = "rofl";
pub.doStuff = function(x, y) {
return this.doPrivateStuff(x + 1, y + 1) + ' ' + this.somePrivateProp;
};
});
You can play around with this in the console and see that it works like you'd probably expect.
var foo = new Foo('abc', 123);
foo.doStuff(3, 5); // "10 lol"
foo.doPrivateStuff(3, 5) // throws TypeError