Skip to content Skip to sidebar Skip to footer

Prototype Closure In The Constructor

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

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:

  1. The public scope object is placed in the prototype chain of the private scope object.
  2. 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:

  1. The private scope object is placed in the prototype chain of the public scope object.
  2. All functions are bound to the private scope object.
  3. 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"