javascriptoopprivate-membersprivileged-functions

Is there any way to make use of information hiding while not wasting memory in Javascript prototypes?


I'm following Crockford's guide to private methods in Javascript, and I'm struggling with something. I'm trying to optimize this code

function Container(param) {

    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    this.member = param;
    var secret = 3;
    var that = this;

    this.service = function () {
        return dec() ? that.member : null;
    };
}

by defining the functions outside of the constructor so that a new function object isn't created each time a new instance is created.

I still have no idea of how to do this for those he refers to as private methods (any help is well appreciated). For those he calls privileged methods this is what I'm trying to do:

function Container(param) {

    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    this.member = param;
    var secret = 3;
    var that = this;
}
Container.prototype.service = function() {
    return dec() ? that.member : null; 
};

but if I test it like this

d1 = new Container("content");
d1.service();

I get this error:

ReferenceError: dec is not defined

Does this mean there's no way of using the advantages of the private/privileged methods Crockford uses AND optimizing memory usage by linking all instances of the class to the same function object? I hope you'll prove me wrong.


Solution

  • If you don't want to create a new dec on every call of Container, you can make Container into an IIFE - define dec when Container is defined, and then return the actual Container constructor from the IIFE, so that dec is only referenceable from inside the Container. To encapsulate the secrets, use a Map indexed by instance instead of using a plain var secrets in the constructor, so that the (shared) dec and service functions can see and use the Map.

    Actually, as comment notes, it'd probably be better to use a WeakMap so that an instance's associated secret can be garbage collected once the instance is GC'd:

    const Container = (() => {
      const secretsByInstance = new WeakMap();
      function dec(instance) {
        const secret = secretsByInstance.get(instance);
        if (secret > 0) {
          secretsByInstance.set(instance, secret - 1);
          return true;
        } else {
          return false;
        }
      }
      function Container(param) {
        secretsByInstance.set(this, 3);
        this.member = param;
      }
      Container.prototype.service = function() {
       return dec(this) ? this.member : null;
      };
      return Container;
    })();
    
    d1 = new Container("content");
    console.log(d1.service());
    console.log(d1.service());
    console.log(d1.service());
    console.log(d1.service());