15

I'm trying to get a deeper hold on prototypal inheritance and class creation (I know, there are other ways, but for the purpose of this I'm trying to grasp prototypes.) My question is: Using the following code example, is there a way to create private variables inside of Tree and Fruit that will not be returned with the function, but is still accessible to the prototype functions genus and bulk?

var Tree = function ( name, size ) { 
    this.name = name;
    this.size = size;
};

Tree.prototype.genus = function(){
    return ((typeof this.name !== 'undefined') ? this.name : 'Hybridicus Maximus');
};
Tree.prototype.bulk = function(){
    return ((typeof this.size !== 'undefined') ? this.size : '8') + ' ft';
};


var Fruit = function( name, size ) { 
    this.name = name;
    this.size = size;
};

Fruit.prototype = new Tree();
// Fruit.prototype = Tree.prototype; -- I know this can be used, too.

Fruit.prototype.bulk =  function(){
    return ((typeof this.size !== 'undefined') ? Math.floor(this.size / 2) : '4') + ' lbs';
};

var pine = new Tree('Pine', 9);
var apple = new Fruit('Apple', 6);

console.log(pine.genus(), pine.bulk()); // Outputs: "Pine 9 ft"
console.log(apple.genus(), apple.bulk()); // Outputs: "Apple 3 lbs"

EDIT: I'm trying to replace this.name and this.size with private variables that can be accessed in the prototype functions. Sorry for the lack of clarity!

buzzedword
  • 3,108
  • 3
  • 21
  • 26
  • Could you not tag language questions "reinforcement-learning"? – Don Reba Jun 10 '11 at 20:57
  • 1
    @Don-Reba, yeah, sorry about that. I tagged it as "learning" and it must've bumped it to reinforcement. *EDIT* They have an appropriate tag now for prototypal-inheritance that wasn't available when I made this. Switched to it. Sorry for the confusion. – buzzedword Jun 13 '11 at 14:33
  • possible duplicate of [Possible to access private constructor-scoped variables from a functions prototype?](http://stackoverflow.com/questions/7788175/possible-to-access-private-constructor-scoped-variables-from-a-functions-prototy) – educampver Oct 10 '13 at 04:04
  • @ecampver yeah, they asked the same question I did. – buzzedword Jan 15 '14 at 22:59
  • Actually, there is a way. Check [this answer](http://stackoverflow.com/a/24815500/178749). – Duarte Cunha Leão Jul 18 '14 at 00:11
  • Taking this opportunity I have attached a very late but a very simple answer to show how JS prototypical inheritance is priceless. Check it out at http://stackoverflow.com/questions/6307684/how-to-create-private-variable-accessible-to-prototype-function/#answer-35826569 – Redu Apr 07 '16 at 08:54

4 Answers4

14

Yes. You can do this:

(function() {
  var private = "hi";

  Tree.prototype.genus = function(){
    return ((typeof this.name !== 'undefined') ? this.name : 'Hybridicus Maximus');
  };
  Tree.prototype.bulk = function(){
    return ((typeof this.size !== 'undefined') ? this.size : '8') + ' ft';
  };
})();

Now, that'll provide a private variable that those functions can see, but it'll be a private "class" variable - all instances will share the same variable, in other words. If you want a private variable per instance, you have to do that in the constructor (or "init" method, or whatever), meaning the methods that share those privates would also have to be created there. (You could of course put a function on the prototype that would create the instance methods at construction time.)

edit — One thing you could do is use a technique like this to build a mechanism like jQuery's ".data()", so that you'd have a class variable that acts as a place to keep per-instance values. It'd be kind-of clunky, but it'd be workable.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Sorry-- added a clarity edit. Trying to replace this.name and this.size with the private variables. – buzzedword Jun 10 '11 at 14:45
  • I _do_ like this though, this is a very useful pattern for a few other projects that use prototypes. Thanks! -- upvoted. – buzzedword Jun 10 '11 at 14:56
2

This is what I wrote about in a blog post about Classes, Private Members, & Prototypal Inheritance in JavaScript. Basically you want to create a private variable accessor function unique to every object and then have those prototype methods call that private accessor function, supplying it with the key that is only available within the closure:

(function(_) {
  Tree = function ( name, size ) { 
    var hidden = {
      name: name,
      size: size
    };
    this._ = function($) {
      return _ === $ && hidden;
    };
  };

  Tree.prototype.genus = function(){
    return ((typeof this._(_).name !== 'undefined') ? this._(_).name : 'Hybridicus Maximus');
  };
  Tree.prototype.bulk = function(){
    return ((typeof this._(_).size !== 'undefined') ? this._(_).size : '8') + ' ft';
  };

  Fruit = function( name, size ) { 
    Tree.apply(this, arguments);
  };
  Fruit.prototype = new Tree();
  // Fruit.prototype = Tree.prototype; -- I know this can be used, too.

  Fruit.prototype.bulk =  function(){
    return ((typeof this._(_).size !== 'undefined') ? Math.floor(this._(_).size / 2) : '4') + ' lbs';
  };
})({});



var pine = new Tree('Pine', 9);
var apple = new Fruit('Apple', 6);

console.log(pine.genus(), pine.bulk()); // Outputs: "Pine 9 ft"
console.log(apple.genus(), apple.bulk()); // Outputs: "Apple 3 lbs"

console.log(pine._(), pine._({})); // Outputs: "false false" because outside of closure

You will notice that the last line shows that private variables are not accessible outside of the closure and thusly can't be retrieved by third-party code unless made available by an accessor function.

Chris West
  • 885
  • 8
  • 17
  • 2
    You still can hijack that local key object (`_`) by overwriting the `._` method and then access all "hidden" data. – Bergi Feb 07 '13 at 21:24
  • Is this true prototypal inheritance anymore though? Each instance of "Tree" will be based on a different constructor function (since it's a different closure) and thus no two Tree instances will share the same prototype. I think the memory overhead is basically the same as just adding all those functions directly to `this` in the constructor (and then you could use local variables in the constructor for your private fields, which is simpler). – peterflynn Dec 06 '17 at 01:14
0

It can be easily achieved like this

function SharedPrivate(){
  var private = "secret";
  this.constructor.prototype.getP = function(){return private}
  this.constructor.prototype.setP = function(v){ private = v;}
}

var o1 = new SharedPrivate();
var o2 = new SharedPrivate();

console.log(o1.getP()); // secret
console.log(o2.getP()); // secret
o1.setP("Pentax Full Frame K1 is on sale..!");
console.log(o1.getP()); // Pentax Full Frame K1 is on sale..!
console.log(o2.getP()); // Pentax Full Frame K1 is on sale..!
o2.setP("And it's only for $1,795._");
console.log(o1.getP()); // And it's only for $1,795._

Obviously the key point is to create an access route to a private variable by utilizing a closure and then sharing this access point among the objects to be created. Utilizing the access point as a prototype of the objects to be created for natural sharing is the ideal case. Accordingly the same functionality can be achieved by utilizing the factory pattern and Object.create() as follows;

function SharedPrivate(){
var priv = "secret";
return {gp : function(){return priv},
        sp : function(v){priv = v}
       }
}
sharedProto = SharedPrivate(); // priv is now under closure to be shared
var p1 = Object.create(sharedProto); // sharedProto becomes o1.__proto__
var p2 = Object.create(sharedProto); // sharedProto becomes o2.__proto__

JavaScript prototypical structure is golden..!

Redu
  • 25,060
  • 6
  • 56
  • 76
  • 2
    Never. ever. assign to prototype properties from within your constructor. – Bergi Apr 07 '16 at 23:32
  • @Bergi Thanks for the comment... I keep hearing this cliche time to time yet neither could think of a justifiable reason myself nor heard anybody talking more than this sentence. I tend to believe it's nothing more than a gossip in the air. Could you please elaborate me on what are the dangers of adding two tiny functions to the prototype of a function that's also created by myself. – Redu Apr 08 '16 at 09:13
  • It's not a cliche: it just doesn't make sense, and [it has undesirable behaviour](http://stackoverflow.com/questions/28255957/assigning-prototype-methods-inside-the-constructor-function-why-not). You already seem to understand this, as you named that class "*Shared*Private", and even seem to expect the usually undesired behaviour where `o1` interferes with `o2`; but even if you want to create a static, shared variable your approach doesn't work. Just try to create an `o3` somewhere in that sequence of calls… The problem is not with adding to the prototype, but with doing so in the constructor. – Bergi Apr 08 '16 at 10:35
  • Thank you for taking your time to explain. My bad.. i forgot to mention that my critic to the "Never. ever. assign to prototype properties from within your constructor" sentence was only related as per the pattern i have shown above. Otherwise of course you shouldn't modify the prototype from within the constructor. However, makes sense or not, if you need to create a shared private among several objects, then they have to be created at once together since as you have also figured out. There is no way to do this with a constructor without touching the prototype within itself. – Redu Apr 08 '16 at 11:01
  • No, the sentence did not just relate to your specific code. There really is *never* a good reason to do that - "*Otherwise of course you should modify the prototype from within the constructor.*" is plainly wrong. Or can you name a reason? If you want to share state across instances, you shouldn't create that state in (the last call of) the constructor. Just put it outside of the constructor, and wrap everything in an IIFE if you need privacy. – Bergi Apr 08 '16 at 11:07
  • @Bergi... that was my typo... i corrected that sentence 45 seconds after i posted the comment when i read it over. Just amazed how fast you read my reply :) Well anyways... i guess the factory function and Object.create() way might be preferable. – Redu Apr 08 '16 at 11:17
  • It's not about what's right or wrong or trendy, as you imply; what tangible reason do you have for doing it your way? Every time a new instance of this function is created you're needlessly assigning two new prototypes when you could just do it once by moving them outside of the constructor. There is no disadvantage to doing this and it's far more verbose to assign them outside of the constructor. What happens if you need a hundred new SharedPrivate? That's 198 unnecessary assignments. What about a thousand? Why go to the effort of optimising later when you can optimise as you go along? – Woody Payne Sep 30 '17 at 12:15
-1

i made up this based on your title of question, not the content you gave. i had the same question here.

var Tree = function(){

    var outprivatename = "bigsecret"

    var Tre = function(nickname){
        var privatename = "secret that no one should know" 
        this.nickname = nickname
        outprivatename = outprivatename + "-->" + nickname
        this.deepsecret=function(){return privatename + "---" + nickname}
    }

    Tre.prototype.getname=function(){
        console.log(outprivatename+'-----'+this.nickname)
        // console.log(privatename)
      return this.nickname
    }
    return Tre

}
//case one: each has unique closure; `outprivatename` is different for them
var pine = new (Tree())('pine')
var apple = new (Tree())('apple')
pine.getname() //bigsecret-->pine-----pine
apple.getname() //bigsecret-->apple-----apple
console.log(pine.deepsecret()) //secret that no one should know---pine
console.log(apple.deepsecret()) //secret that no one should know---apple

//case one: two share one closure; `outprivatename` is same for both
var Hybrid = Tree()
var hybrid1 = new Hybrid("pinapple1")
var hybrid2 = new Hybrid("pinapple2")

hybrid1.getname() //bigsecret-->pinapple1-->pinapple2-----pinapple1
hybrid2.getname() //bigsecret-->pinapple1-->pinapple2-----pinapple2
console.log(hybrid1.deepsecret()) //secret that no one should know---pinapple1
console.log(hybrid2.deepsecret()) //secret that no one should know---pinapple2

basically the Tree function provided a closure for the constructor Tre inside of it. Instead of using the deepest privatename, you can use the one that's outside of Tre but inside of the anonymous function Tree.

MartianMartian
  • 1,753
  • 1
  • 18
  • 26