0

Just trying to wrap my head around prototype-based design

Problem: implement a data structure say priority-queue with a known API. Instantiate multiple instances of the PQ.

So I used the revealing module pattern as follows

module.exports = (function () {
// ... assume the following methods are revealed. Other private methods/fields are hidden
let priorityQueue = {
        insert,
        removeMax,
        isEmpty,
        toString
    };

    return {
        priorityQueue,
        newObj: (comparer, swapper) => {
            let instance = Object.create(priorityQueue);
            instance.array = [];
            instance.size = 0;
            instance.less = comparer;
            instance.swap = swapper;
            return instance;
        }
    }
})();

Created a newObj factory method to create valid instances. priorityQueue is the API/prototype.

  • So methods belong in the prototype.
  • Instance Fields cannot reside there ; they would be shared across instances.

However in this case, the internal fields of the PQ are not encapsulated.

const pQ = require('./priorityQueue').newObj(less, swap);
pQ.array = undefined;    // NOOOOOOO!!!!

Update: To clarify my question, the methods in the prototype object need to operate on the instance fields array & size. However these fields cannot be shared across instances. How would the methods in the prototype close over instance fields in the object?

Gishu
  • 134,492
  • 47
  • 225
  • 308
  • 1
    Possible duplicate of [Private properties in JavaScript ES6 classes](https://stackoverflow.com/questions/22156326/private-properties-in-javascript-es6-classes) – Heretic Monkey Feb 17 '18 at 20:34
  • 1
    You've got everything here: https://crockford.com/javascript/private.html – RaphaMex Feb 17 '18 at 20:47
  • while i agree on @RaphaMex link to be serious foundation knowledge about javascript everyone has to obtain to call himself a guru, i'd advice to use es6 classes theese day. they essentially do the same but with far less code. – GottZ Feb 18 '18 at 16:17
  • @GottZ - right. I have seen the new ES6 class syntax. Just trying to figure out how it was done pre ES6. Trying to see if this can be done with minimal use of constructor functions - simple objects linked via prototype links. – Gishu Feb 18 '18 at 17:01

2 Answers2

1

Don't assign array or whatever you want to encapsulate to new object.

module.exports = (function () {
    // ... assume the following methods are revealed. Other private methods/fields are hidden
    let priorityQueue = {
        insert,
        removeMax,
        isEmpty,
        toString
    };

    return {
       priorityQueue,
       newObj: function(comparer, swapper){
         let array = [];

         let instance = Object.create(priorityQueue);
         instance.size = 0;
         instance.less = comparer;
         instance.swap = swapper;
         return instance;

    }
}
})();
Mike Ezzati
  • 2,968
  • 1
  • 23
  • 34
  • And if you want to give access to those private properties, create a getter method, like `instance.getArray = function() { return array; }` – oniramarf Feb 17 '18 at 20:07
  • 3
    @oniramarf A getter that makes a mutable object accessible is not much better than just a public property – Bergi Feb 17 '18 at 21:22
  • @Bergi yeah, I did not think that `array` it's an object and all objects are always handled by reference in Javascript. It would work with a primitive type though, like the size property. – oniramarf Feb 17 '18 at 22:06
  • Not sure I understand this, the array is the backing store for the priority queue. All operations such as insert or removeMax operate on the array. Each priority queue instance should have its own set of items - how would the methods in prototype object access the local variable in newObj? – Gishu Feb 18 '18 at 16:04
  • This way per each new instance you are creating an array object. This array object is not a member of your instance but only methods of your instance have exclusive access to it. As long as you don't expose this array object lo outer context you can treat it as a private member of your instances. – Mike Ezzati Feb 20 '18 at 07:56
0

the reason class syntax was implemented directly into js was just to remove the need to seek that answer. if you really want to go that deep, you should just read the book i mentioned below my answer.

to give you an example of intentional usage of closures to grant private data, i'm going to create a little code example just for this occasion.

keep in mind it's just an example of a concept and it's not feature complete at all. i encourage you just to see it as an example. you still have to manage instances because the garbage collector will not clean them up.

// this will be the "class"
const Thing = (function(){
    // everything here will be module scope.
    // only Thing itself and it's instances can access data in here.
    const instances = [];
    // private is a reserved word btw.
    const priv = [];

    // let's create some prototype stuffz for Thing.
    const proto = {};
    // this function will access something from the module scope.
    // does not matter if it's a function or a lambda.
    proto.instanceCount = _=> instances.length;
    // you need to use functions if you want proper "this" references to the instance of something.
    proto.foo = function foo() {return priv[instances.indexOf(this)].bar};

    const Thing = function Thing(arg) {
        // totally will cause a memory leak
        // unless you clean up the contents through a deconstructor.
        // since "priv" and "instances" are not accessible from the outside
        // the following is similar to actual private scoping
        instances.push(this);
        priv.push({
            bar: arg
        });
    };
    
    // let's assign the prototype:
    Thing.prototype = proto;

    // now let us return the constructor.
    return Thing;
})();

// now let us use this thing..
const x = new Thing('bla');
const y = new Thing('nom');

console.log(x.foo());
console.log(x.instanceCount());
console.log(y.foo());

there is a great book called "Pro Javascript Design Patterns" by Dustin Diaz and Ross Harmes. it's open free theese days: https://github.com/Apress/pro-javascript-design-patterns

it will in depth explain certain design patterns that aimed to solve exactly this answer long before we got classes etc. in javascript.

but honestly.. if you want to go further and add something like "extend" or calling functions of the super class.. dude srsly.. just use classes in js. yes it's all possible in plain vanilla but you don't want to go through all the hassle of creating gluecode.

GottZ
  • 4,824
  • 1
  • 36
  • 46