3

Since my last question, I've been studying Javascript's prototype model and trying to get rid of the OOP vision I inherited from other languages (pun slightly intended).

I went back to basics and read Crookford's Javascript: The Good Parts, along with You Don't Know JS material and decided to stick with the so called behaviour delegation.

Restructuring my previous example implementing behaviour delegation and namespacing, I wrote:

var GAME = {};

(function(namespace) {
    var Warrior = {};

    Warrior.init = function(weapon) {
        this.setWeapon(weapon);
    };

    Warrior.getWeapon = function() {
      return this.weapon;
    };

    Warrior.setWeapon = function(value) {
      this.weapon = value || "Bare hands";
    };

    namespace.Warrior = namespace.Warrior || Warrior;
})(GAME);

(function(namespace) {
    var Archer = Object.create(namespace.Warrior);

    Archer.init = function(accuracy) {
        this.setWeapon("Bow");
        this.setAccuracy(accuracy);
    };

    Archer.getAccuracy = function() {
        return this.accuracy;
    };

    Archer.setAccuracy = function(value) {
      this.accuracy = value;
    };

    namespace.Archer = namespace.Archer || Archer;
})(GAME);

So, everytime I copy a new Archer object:

var archer1 = Object.create(GAME.Archer);

only this object will be created, conserving memory.

But what if I don't want to expose "accuracy" attribute? The attribute would only increase by calling a "training()" method or something similar. I tried to use var accuracy inside the anonymous function, but it turns into kind of static variable, all instances of Archer would share the same value.

The question: Is there any way to set a variable as private while still keeping behaviour-delegation/prototypal pattern?

I do know of functional pattern as well, Here I succesfully achieved variable privacy, at the cost of memory. By going functional, every new "archer" instance generates a new "Warrior" and then a new "Archer". Even considering that Chrome and Firefox have different optmizations, testings on both report that the Delegation/Prototypal pattern is more efficient:

http://jsperf.com/delegation-vs-functional-pattern

If I go with the pure-object delegation pattern, should I just forget the classic encapsulation concept and accept the free changing nature of properties?

Community
  • 1
  • 1
adrield
  • 629
  • 6
  • 19
  • Honestly I don't get why people fight constructors so much....`Object.create` is so much slower than `new` in most engines anyway. – plalx Sep 23 '15 at 20:20
  • "OLOO supports better the principle of separation of concerns, where creation and initialization are not necessarily conflated into the same operation." That's a ridiculous claim to me. Creating objects that are left uninitialized is asking for trouble. – plalx Sep 23 '15 at 20:35
  • Doesn't this pattern totally break the [`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) operator? – Drew Gaynor Sep 23 '15 at 20:42
  • "From a design pattern perspective, we didn't share the same method name render(..) in both objects, the way classes suggest, but instead we chose different names ( insert(..) and build(..) ) that were more descriptive of what task each does specifically. The initialization methods are called init(..) and setup(..) , respectively, for the same reasons." That's so funny... how is `setup` more suitable for `Button` than `init`, that's just arbitrary... – plalx Sep 23 '15 at 20:53
  • @plalx I see you don't like the OLOO pattern, but is not my intent to evangelize it. Actually, my question just pointed out a concern I have with it, about private variables. That's my actual concern and what I tried to express. How would you deal with the problem, using constructors? About speed on Object.create and 'new', I saw JSPerf tests, different browser had different results: Firefox was faster with the former, Chrome was faster with 'new'. I could also create a helper function to initialize the objects as soon as they are created, no problem with that as well. – adrield Sep 23 '15 at 21:02
  • @DrewGaynor I'm not sure about instanceof operator. I saw isPrototypeOf() and getprototypeOf() methods, is there any disadvantages in using them instead of the operator? – adrield Sep 23 '15 at 21:05
  • 1
    @adrield It's not that I do not like the pattern, but they advertise it like if it's so different than using `new` while at the end it's all prototypal inheritance. On top of that they try to present obvious weaknesses of the pattern as strengths. Anyway, to answer your question I wouldn't bother about true privacy at the instance level in JS. Relying on a naming convention such as `this._myPrivateVar` is much more pragmatic. Enforcing true privacy at the module level is fine however. – plalx Sep 23 '15 at 21:44
  • 1
    If you still want to do it... `Archer.init = function(accuracy) { var _accuracy = accuracy; this.getAccuracy = function () { return _accuracy; }; };` – plalx Sep 23 '15 at 21:49

1 Answers1

1

I would try to answer your question with something that is slightly different then this one that tells you how to use a library. Different in that it will(hopefully) give you some ideas of how can we tackle the problem of private vars in OLOO ourselves. At least to some extent, with our own code, no external lib needed, which could be useful in certain scenarios.


In order for code to be cleaner I've striped your anonymous wrapper functions, since they are not related to problem in any way.

var Warrior = {};

Warrior.warInit = function (weapon){
   this.setWeapon(weapon);    
}

Warrior.getWeapon = function(){
   return this.weapon;
}

Warrior.setWeapon = function (value){
   this.weapon = value || "Bare hands";
}

var Archer = Object.create(Warrior);

Archer.archInit = function (accuracy){
   this.setWeapon("Bow");   
   this.setAccuracy(accuracy); 
}

Archer.getAccuracy = function (pocket) {
   return pocket.accuracy;
}
Archer.setAccuracy = function (value, pocket){
   pocket.accuracy = value;
}

function attachPocket(){ 

   var pocket = {};

   var archer = Object.create(Archer); 

   archer.getAccuracy = function(){
      var args = Array.prototype.slice.call(arguments);
      args = args.concat([pocket]); 

      return Archer.getAccuracy.apply(this, args)
   }; 
   archer.setAccuracy = function(){ 
      var args = Array.prototype.slice.call(arguments);
      args = args.concat([pocket]); 

      return Archer.setAccuracy.apply(this, args);
   }; 

   return archer;
}


var archer1 = attachPocket();  

archer1.archInit("accuracy high"); 
console.log(archer1.getAccuracy()); // accuracy high
archer1.setAccuracy("accuracy medium");
console.log(archer1.getAccuracy()); // accuracy medium

Test code above here. (and open your browser console)

Usage

1 ) General practice in OLOO about naming functions on different levels of prototype chain is apposite from OOP. We want different names that are more descriptive and self documenting, which brigs code that is cleaner and more readable. More importantly, by giving different names we avoid recursion loop:

Archer.init = function(accuracy, pocket){
     this.init() // here we reference Archer.init() again, indefinite recurson. Not what we want 
      ...
}
Archer.archInit = fucntion (accuracy, pocket){ // better,
     this.warInit() // no name "collisions" .
}

2 ) We've created an attachPocket() function that creates internal variable pocket. Creates new object with Object.create() and sets it's prototype to point to Archer. Pause. If you notice, functions that required a private var we have defined so that each of them take one more parameter(pocket), some use just pocket. Here is the trick.

By making wrapper functions like archer.setAccuracy(), archer.getAccuracy() ... we can create closures and call directly functions that need private var (here pocket) and pass it to them as an argument.

Like so:

 function AtachPocket(){
   ...
   var pocket = {};

   archer.setAccuracy = function(){ 
     var args = Array.prototype.slice.call(arguments);
     args = args.concat([pocket]); // appending pocket to args
     return Archer.setAccuracy(this, args); 
   }; 
   ...
 }

Essencially by doing this we are bypassing what would have been a normal search for functions in prototype chain, just for functions that have need for a private var. This is what "call directly" refers to. By setting the same name for a function in archer("instance") like it is in prototype chain (Archer) we are shadowing that function at the instance level. No danger of having indefinite loops, since we are "calling directly" like stated above. Also by having the same function name we preserve the normal, expected behaviour of having access to same function in an "instance" like it is in a prototype chain. Meaning that afther var archer = Object.create(Archer) we have access to function setAccuracy like we would if it had been normal search for function in prototype chain.

3 ) Every time attachPocket() is invoked it creates a new "instance" that has those wrapper functions that pass a pocket argument (all as an internal detail of implementation). And therefore every instance has own, unique, private variable.

You would use functions in an "instance" normally:

archer1.archInit("accuracy high"); // Passing needed arguments.
                               // Placed into pocked internally.
archer1.getAccuracy(); // Getting accuracy from pocket.

Scalability

Up to now all we have is function that "attaches a pocket" with hardcoded values like Archer.setAccuracy, Archer.getAccuracy. What if we would like to expand prototype chain by introducing a new object type like this var AdvancedArcher = Object.create(Archer), how the attachPocket is going to behave if we pass to it AdvancedArcher object that might not even have setAccuracy() function? Are we going to change attachPocket() each time we introduce some change in prototype chain ?

Let's try to answer those questions, by making attachPocket() more general.


First, expand prototype chain.

var AdvancedArcher = Object.create(Archer);

AdvancedArcher.advInit = function(range, accuracy){
    this.archInit(accuracy);
    this.setShotRange(range);
}
AdvancedArcher.setShotRange = function(val){
    this.shotRange = val;
}

More generic attachPocket.

function attachPocketGen(warriorType){

   var funcsForPocket = Array.prototype.slice.call(arguments,1); // Take functions that need pocket
   var len = funcsForPocket.length; 

   var pocket = {};
   var archer = Object.create(warriorType); // Linking prototype chain

   for (var i = 0; i < len; i++){ // You could use ES6 "let" here instead of IIFE below, for same effect
      (function(){  
         var func = funcsForPocket[i]; 
         archer[func] = function(){ 
             var args = Array.prototype.slice.call(arguments);
             args = args.concat([pocket]); // appending pocket to args

             return warriorType[func].apply(this, args);
         }
      })()
   }

   return archer;
}

 var archer1 = attachPocketGen(Archer,"getAccuracy","setAccuracy");  

archer1.advInit("11","accuracy high"); 
console.log(archer1.getAccuracy()); // "accuracy high";

archer1.setAccuracy("accuracy medium");
console.log(archer1.getAccuracy());

Test the code here.

In this more generic attachPocketGen as first argument we have a warriorType variable that represents any object in our prototype chain. Arguments that may fallow are ones that represent names of functions that need a private var.

attachPocketGen takes those function names and makes wrapper functions with same names in archer "instance". Shadowing, just like before. Another thing to recognise is that this model of making wrapper functions and using apply() function to pass variables from closures is going to work for functions that use just pocket, functions that use pocket and other variables, and when ,of course, those variables use the relative this reference in front of them. So we have achieved somewhat more usable attachPocket, but that are still things that should be noticed.

1) By having to pass names of functions that need private var, that usage implies that we(attachPocketGen users) need to know whole prototype chain (so we could see what functions need private var). Therefore if you are to make a prototype chain like the one here and just pass the attachPocketGen as an API to the programmer that wants to use your behaviour-delegation-with-private-variables, he/she would had to analyse objects in prototype chain. Sometimes that is not what wee want.

1a) But we could instead, when defining our functions in prototype chain (like Archer.getAccuracy) to add one property to them like a flag that can tell if that function have need for a private var:

Archer.getAccuracy.flg = true;

And then we could add additional logic that checks all functions in prototype chain that have this flg and fills the funcsForPocket. Result would be to have just this call:

var archer1 = attachPocketGen(AdvancedArcher)

No other arguments except warriorType. No need for user of this function to have to know how prototype chain looks like, that is what functions have need for a private var.

Improved style

If we are to look at this code:

Archer.archInit = function (accuracy){
   this.setWeapon("Bow");   
   this.setAccuracy(accuracy); 
}

We see that it uses "pocket" function setAccuracy. But we are not adding pocket here as last argument to it because setAccuracy that gets called is shadowed version, the one from instance. Becouse it will be called only from an instance like so archer1.archInit(...) That one is already adding a pocket as last argument so there is on need to. That's kinda nice, but the definition of it is:

Archer.setAccuracy = function (value, pocket){ ...

and that can be confusing when making objects in prototype chain like Archer.archInit above. If we look the definition of setAccuracy it looks like we should it. So in order not have to remember that we don't have to add pocket as last arg in functions (like archInit) that use other pocket functions, maybe we should to something like this:

Archer.setAccuracy = function (value){  
   var pocket = arguments[arguments.length -1];

   pocket.accuracy = value;
}

Test the code here.

No pocket as last argument in function definition. Now it's clear that function doesn't have to be called with pocket as an argument wherever in code.

1 ) There are other arguably minor stuff to refer to when we think about more general prototype chain and attachPocketGen.Like making functions that use pocket callable when we dont wan't to pass them a pocket, that is to toggle pocket usage to a function, but in order not to make this post too long, let's pause it.

Hope this can give you view ideas for solution to your question.

  • Sorry for taking so long, I though the topic was dead after a while! Very elaborate answer and made in vanilla JS, which is important for me. So, as I understood, private variables are created at instance level and the wrapper generates that instance, instead of attaching the variable to an existing "class". I wonder if things would get too complicated if later I decided to add another private variable, like "pet". Unless the instance could be passed down to each "private wrapper" functions like adoptPet(attachPocket(...), ...) and so on. – adrield May 05 '17 at 13:25
  • The private variable (pocket) is created in same function scope where "instance" is created. Instance is created by AttachPocket function. When you invoke those wrapper function from instance their internal logic just adds a private var to arguments of some particular function. See example from: Usage 2) The pocket is an object. That means that you could put in pretty much what ever objects, arrays etc. you need. Every thing you want to be private you put in as a property of "pocket" object. Archer.somefunc(yourArg1, yourArg2, pocket){ pocket.pet = yourStuff ... } – psychoActivePigtaur May 05 '17 at 14:43
  • @adrield So the idea is that when making "class" and class functions you put in their definition one more arg at the end (arguably it's better to put it in the beginng as first argument to your function). Internal logic of your "class functions" is dealing with pocket in what ever you see fit. Adding and subtracting private properties. *Key point:* when you have your instance like var archer1 = attachPocket(AdvancedArcher) the user of "archer1" cant do this --> archer1.pocket // undefined. Trough instance, pocket is nowhere to be found and or any of its contents. – psychoActivePigtaur May 05 '17 at 14:46
  • @adrield I will update my answer with one more example and some clarification in next day or two. Also there is another way of doing pretty much the same thing that I will explain how to do it in couple of days. It's a bit simpler then this one. Stay tuned if interested. – psychoActivePigtaur May 05 '17 at 14:53
  • @adrield Also to make myself clear: `Warrior.pocket // undefined` `Archer.pocket // undefined` `AdvancedArcher.pocket // undefined` In OLOO object linked in a prototype chain like warrior-archer thing here serve just as repositories of abilities/actions for your instance. Every "this" reference in those abilites is contextual. It refers to what ever instance is accessing them trough prototype chain. – psychoActivePigtaur May 05 '17 at 15:47
  • @adrield Shortly: when you want to add `adoptPet()` , you decide to what object in prototype chain it belongs (by action it does or what ever your criteria is). Let's say it you want to add it in `Archer`, you would do this: `Archer.adoptPet = function (yourArgsIfAny, pocket){ if( pocket.accuracy > 11} pocket.pet = doYourStuff with yourArgsIfAny } ` Then you add it to your AttachPocket call: `var archer1 = AttachPocket(Archer, "otherPocketFuncThereMayBe", "adoptPet" )` – psychoActivePigtaur May 05 '17 at 19:22