Private Variables Do Not Necessarily Break Prototypal Inheritance In Javascript

Posted April 28, 2011 at 2:00 PM by Ben Nadel

Tags: Javascript / DHTML

After blogging this morning about how poor constructor logic can break prototypal inheritance in Javascript, I wanted to clarify that I was not trying to imply that the use of "private" variables breaks prototypal inheritance. At least, not necessarily. In my previous post, my "poor choice" demo did include private variables; however, this was only to create a context in which one might return an object literal from a constructor. Private variables are totally fine in the context of the prototype chain so long as your object's API is still this-scoped.

If you want to see a use of private variables that does break prototypal inheritance, take a look at my previous post. But, below, I'll show you a use of private variables that keeps the initialization chain in tact; which, in turn, allows for proper inheritance.

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Constructor Logic Is Critical For Prototypal Inheritance</title>
  • <script type="text/javascript">
  •  
  •  
  • // I am the person constructor.
  • function Person( name ){
  •  
  • // Create a **PRIVATE** property for name.
  • var name = arguments[ 1 ];
  •  
  • // Create a **PRIVATE** property for traits.
  • var traits = {};
  •  
  •  
  • // Create a method for accessing the name.
  • //
  • // NOTE: Since this method is this-scoped, each
  • // *intialized* instance will get its own copy.
  • this.getName = function(){
  • return( name );
  • };
  •  
  •  
  • // Create a method for accessing / mutating the
  • // traits collection.
  • //
  • // NOTE: Since this method is this-scoped, each
  • // *intialized* instance will get its own copy.
  • this.trait = function( name, value ){
  •  
  • // Check to see if we are getting or setting
  • // the given trait for this person.
  • if (arguments.length == 2){
  •  
  • // Set the trait.
  • traits[ name ] = value;
  •  
  • // Return this object.
  • return( this );
  •  
  • } else {
  •  
  • // Return the given triat.
  • return( traits[ name ] );
  •  
  • }
  •  
  • };
  •  
  •  
  • // Create a way to access the traits (for debugging).
  • //
  • // NOTE: Since this method is this-scoped, each
  • // *intialized* instance will get its own copy.
  • this.getTraits = function(){
  • return( traits );
  • };
  •  
  • }
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I am the girl contructor.
  • function Girl( name, age, weight ){
  •  
  • // Call the super constructor to initiate base class.
  • Person.call( this, name );
  •  
  • // Store the additional properties.
  • this.trait( "age", (age - 5) );
  • this.trait( "weight", (weight - 10) );
  •  
  • }
  •  
  • // Extend the Person class.
  • Girl.prototype = new Person();
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Create some girl instances.
  • var sarah = new Girl( "Sarah", 32, 115 );
  • var tricia = new Girl( "Tricia", 30, 125 );
  •  
  • // Log the girls' traits.
  • console.log( "Sarah:", sarah.getTraits() );
  • console.log( "Tricia:", tricia.getTraits() );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

As you can see, the Person() constructor declares two private variables - name and traits. But, the reason that the inheritance works is because the API for the object - getName(), trait(), getTraits() - is still declared in the "this" scope. In doing so, the sub-class can still be assigned unique methods that point to unique members as a byproduct of the super-class' constructor.

When we run the above code, we get the following console output:

Sarah: Object { age=27, weight=105 }
Tricia: Object { age=25, weight=115 }

As you can see, both Girl() instances are clearly using a different set of private variables. The initialization chain worked!

I am not personally a fan of lexically-scoped private variables. Frankly, I'm not that good a programmer. I much prefer convention-based private variables (ie. members that start with an underscore "_"). But, I just wanted to clarify that lexically-scoped private variables don't necessarily break prototypal inheritance. Rather, proper initialization chains are based on the way an object's API is architected.




Reader Comments

Apr 28, 2011 at 2:30 PM // reply »
11,246 Comments

While not the point of this post, I thought it might be interesting to point out that in this configuration, the sub-class instances do not have direct access to the super-class private variables; instead, the sub-class instances may only access their super-class variables using the inherited (this-based) API. So, as an example, the Girl() instance cannot access the "name" variable except through the this.getName() method.


Apr 28, 2011 at 2:56 PM // reply »
7 Comments

"Frankly, I'm not that good a programmer."

That made me smile. I think most of us feel this way, Ben. But in reality, I think what makes a good programmer is not really how much you know, rather how much you strive to be the best at what you do and how you approach learning new things.

From my perspective, you are a good programmer because you analyze things to understand them correctly so that you properly implement it in code.


Apr 28, 2011 at 2:59 PM // reply »
11,246 Comments

@TJ,

Ha ha, thanks :) I definitely love to figure all this stuff out! And, prototypal inheritance is definitely a bit of a mind-bender! Just trying to make baby steps.


Apr 28, 2011 at 9:34 PM // reply »
66 Comments

I just know that at some point, one day...maybe way down the road...you're going to start using 'woman' vs. 'girl' in your examples ;)

Just teasing my boy. Oh wait, I mean, my man.

Cheers.


Apr 29, 2011 at 5:15 AM // reply »
2 Comments

Hi Ben.

The problem with this patter is the memory usage. Your `Person' constructor is factory constructor. It adds three instance methods.

// Extend the Person class.
Girl.prototype = new Person();

Now, `Girl.prototype' object has three methods: `getName', `trait' and `getTraits'.

In your `Girl' constructor you have called `Person' constructor again. This time you have called with instance object created trough: `new Girl'. So you recreate those three methods again. Your prototype chain looks:

null
^
|
|
Object.prototype = {};
^
|
|
Person.prototype = {};
^
|
|
Girl.prototype = {
treat : function,
getName : function,
getTreats : function
};
^
|
|
Girl.instance = {
treat : function,
getName : function,
getTreats : function
};

Personally I would not use such a pattern or at least I would create extending pattern with intermediate object in prototype chain instead of calling parent constructor directly.


Apr 29, 2011 at 8:01 AM // reply »
11,246 Comments

@Steve,

It's funny you mention that - I though I had been using "women" for a while??? Why did I stop? I can't remember. Honestly, I think there's something subtle that's hard to type about it. Maybe because I can't just add "S" for plural. I'll try to stay focused :)

@Asen,

I will agree with you that using something like:

  • Girl.prototype = new Person()

... is not the cleanest approach. In my other post, someone pointed out that I should be using Object.create() to define the prototype chain so that I am not actually having to call the super-class constructor.

I agree with this; years and years ago, I was taught to use the "new" approach and I guess old habits die very hard :)

That said, the way you construct the prototype chain does not have much of an affect on the choices made in the constructor functions themselves. And, the point of the post is to show that one can use private variables (locally scoped constructor var's) without breaking inheritance.


Apr 29, 2011 at 12:33 PM // reply »
2 Comments

@Ben,
Yep `Object.create' is possible approach, but it is not supported by old engines which implements ECMA-262 3rd edition.

As a fallback of `Object.create' you could use factory pattern as like this:

var create = (function () {
function F(){}
return function (proto) {
F.prototype = proto;
return new F;
};
})();


Apr 29, 2011 at 3:03 PM // reply »
1 Comments

Maybe I'm missing something, but by creating the functions on the object itself instead of the prototype, you've lost a major reason for going with prototypical inheritance and shouldn't you ar that point adopt Crockford's module pattern? Your approach feels like the worst of both worlds to me.


May 3, 2011 at 10:59 AM // reply »
11,246 Comments

@Asen,

I finally did an exploration of Object.create(). Just to get my feet wet. I am really liking it for creating the prototype chain in the context of constructor-based inheritance.

http://www.bennadel.com/blog/2184-Object-create-Improves-Constructor-Based-Inheritance-In-Javascript-It-Doesn-t-Replace-It.htm

@Matt,

I 1,000% agree with you! I am a major fan of the use of prototype to store methods - as you say, that *is* the reason that the prototype is so awesome. I did not mean to imply that I would use private variables (and the necessary closures) - I was only trying to demonstrate that private variables don't necessarily break prototypal inheritance.


May 4, 2011 at 11:44 AM // reply »
1 Comments

I agree with the title of this post 100%. I have noticed that many people have found many different ways to implement private members in JavaScript, but most of the time, the public getters and setters are defined for each instance. I recently wrote an article which shows how to give prototypal functions access to private members by using code similar to what you have in your example: http://gotochriswest.com/blog/2011/05/04/private-variables-in-javascript/


Jul 22, 2011 at 2:20 PM // reply »
2 Comments

I was experimenting for some time with different patterns, but neither of them so far seems solid to me. In this case I think Matt is right. You do not visibly break prototypal-inheritance, but on practice you do. Unless I miss something important here, the whole point of having prototypal inheritance gets completely wiped out with this pattern in first place. Don't you think?


Feb 2, 2012 at 10:31 PM // reply »
3 Comments

Using this implemenation of inheritance, can I override a parent function in a derived class and then call the parent function from the overriding function?

If so, what would that look like?


May 4, 2012 at 6:51 AM // reply »
1 Comments

The Girl class is not actually related to person in any way, calling

  • Girl.prototype = new Person();

is superfluous since you are doing

  • Person.call( this, name);

. which shadows everything in the prototype.

If you remove the call, the Girl class cannot use its prototype (I.E no inheritance) because the closures in its prototype are tied into one-time environment instead of being available for generic use. You can see that every girl you create will share the same state instead of encapsulating their own data.



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools