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!

6
  • Could you not tag language questions "reinforcement-learning"? Commented Jun 10, 2011 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. Commented Jun 13, 2011 at 14:33
  • possible duplicate of Possible to access private constructor-scoped variables from a functions prototype? Commented Oct 10, 2013 at 4:04
  • @ecampver yeah, they asked the same question I did. Commented Jan 15, 2014 at 22:59
  • Actually, there is a way. Check this answer. Commented Jul 18, 2014 at 0:11

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Sorry-- added a clarity edit. Trying to replace this.name and this.size with the private variables.
I do like this though, this is a very useful pattern for a few other projects that use prototypes. Thanks! -- upvoted.
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.

2 Comments

You still can hijack that local key object (_) by overwriting the ._ method and then access all "hidden" data.
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).
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..!

7 Comments

Never. ever. assign to prototype properties from within your constructor.
@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.
It's not a cliche: it just doesn't make sense, and it has undesirable behaviour. You already seem to understand this, as you named that class "SharedPrivate", 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.
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.
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.
|
-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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.