Prototype Closure In The Constructor
Solution 1:
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
Solution 2:
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.
Solution 3:
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).
functionclassify(fn) {
var privateScope = {}, publicScope = {};
functionbindProp(to, target, src, key) {
if (!src.hasOwnProperty(key)) return;
if (!(src[key] && src[key].bind)) return;
target[key] = src[key].bind(to);
}
functionctor() {
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
functionclassify(fn) {
var privateScope = {}, publicScope = {};
functionbindProp(privateScope, scopeObject, key) {
if (!scopeObject.hasOwnProperty(key)) returntrue;
if (!(scopeObject[key] && scopeObject[key].bind)) return;
privateScope[key] = scopeObject[key].bind(privateScope);
}
functionctor() {
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] = void0;
}
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:
varFoo = 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) {
returnthis.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
Post a Comment for "Prototype Closure In The Constructor"