public
Created

Proxy-based inheritance pattern in JavaScript.

  • Download Gist
inherit-by-proxy.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
function inherit (child, parent) {
function proxy () {};
proxy.prototype = parent.prototype;
child.prototype = new proxy();
};
 
function Parent () {}
function Child () {}
 
inherit(Child, Parent);
 
var child = new Child();
 
console.log(child instanceof Child); // true
console.log(child instanceof Parent); // true

I don't like this pattern for a variety of reasons, which I went into in detail in a 3-part blog post series, titled "JS Objects".

The constructor style of coding that wires objects to each other via [[Prototype]] by calling constructors "works", but it's more complicated and has many more gotchas than just straight object linking via Object.create(). Moreover, the way to think about this mechanism is as "behavior delegation", not "inheritance". If you use constructor-style coding, you opt-in to problems with relative polymorphism, confusion/distraction with the mixin pattern short-circuiting the [[Prototype]] chain, etc. Objects linked to other objects really is the simpler model.

[Update: I'm incorrect asserting an extra object exists in the prototype chain as a result of this pattern. I correct myself in this comment below. Sorry for the noise.]

But setting all that aside, my bigger problem with this pattern is that it "secretly" slips in an anonymous extra link into the [[Prototype]] chain. Child is not directly linked to Parent, but Child -> [anonymous] -> Parent. I'm sure that's the point of you calling this "Proxy". But this means that all objects linked to Child (like child) suffer this same extra linkage weighing them down.

But this anonymous object seems to basically serve no real purpose (unless there's more to this pattern you don't illustrate here) and yet it creates more performance overhead when you "delegate" a call from Child to Parent. Also, creating lots of these sorts of object relations will create more memory overhead, and thus more for the GC to take care of at some point.

I vastly prefer this simpler approach (which I derive in part 3 of that blog post series):

var Parent = {
   // ...
};

var Child = Object.create(Parent);

var child = Object.create(Child);

console.log(Parent.isPrototypeOf(child)); // true
console.log(Child.isPrototypeOf(child)); // true

It's just "OLOO" ("objects linked to other objects")™. :)

OLOO - This has to be a thing!

The function already had an anonymous object attached to it - its default prototype, which had Object.prototype as its [[Prototype]].

This just replaces it with a slightly more useful one, in that it does the same job -- contain properties which will be useful after new is used to set the [[Prototype]] for a new object which is used as the context for a call to the function, when property lookup fails to find an own property in said new object -- but itself has a specific [[Prototype]] which performs the same fallback job at property lookup time!

proxy is long gone anyway at that stage, it's just that you have to bring out the gimp for ES3 :)

@getify: Thanks for your explanation and links. I realize that the Proxy Pattern is old fashioned, and I too prefer your approach for modern environments. I actually feel that this pattern is quite ugly, but that's why I wanted to demonstrate a way to hide the complexity in a three-line function.

Despite the fact that I don't like this pattern, I use it exclusively, and that is for the reason that @insin pointed out: This works in ES3 and Object.create does not. Code I write has to run in IE<9, and that's likely to be the case for quite a while. I care more about compatibility than micro-optimization, and I prefer not to use shims unless there is a very compelling reason to do so.

The reason I prefer the layer of indirection with the anonymous proxy function is to prevent invoking the parent's constructor unnecessarily. In this example doing so would be trivial, but there are a lot of situations where implicitly calling the parent constructor when defining the inheritance chain would be undesirable. Using a proxy lets me define the relationship without side effects for the minor trade-off of a slightly longer lookup time for inherited properties.

We agree that inserting anonymous functions into the prototype chain is not ideal, but I argue that it's likely a trivial performance consideration. Have you done any performance testing to see how the Object.create and proxy patterns compare? I haven't, but I'd love to see what research has been done.

@jeremyckahn

This works in ES3 and Object.create does not

Actually, the non-ES5 polyfilly for Object.create() is:

Object.create = function(o) {
   function F(){}
   F.prototype = o;
   return new F();
};

That way, you can indeed use the Object.create() style of code I suggest is usable in all browsers, even without ES5.

As I look more closely at your above inherit(), I realize that my previous comment too hastily claimed an extra object in the chain that is in fact incorrect. Sorry about that.

Interestingly, your inherit() is almost identical to Object.create(). So:

function Parent(){}
function Child(){}
inherit(Child, Parent);

is functionally identical to:

function Parent(){}
function Child(){}
Child.prototype = Object.create(Parent.prototype);

They're the same. In both cases, there's just two objects, (awkwardly) labeled here as Parent.prototype and Child.prototype, that are linked together via Child's [[Prototype]] chain.

What you notice is that it's the objects we actually care about. The functions and the new calling and all that... that's just unnecessary distractions. The simpler approach:

var Parent = { /*..*/ };
var Child = Object.create(Parent);

Same capability, less confusion. That's why I think it's a superior coding pattern.

What that proves is the point of my articles: you're just doing objects linked to objects, and the fact that those objects happen to be attached to functions (Parent() and Child()) are really irrelevant. I argue that the simpler objects-only approach clears out confusion and misdirection and lets you see clearly and plainly what's really going on.

To me, simpler is better. I don't understand why developers prefer the more verbose and complex way to achieve the same effect. :)


To put a finer point on it, if you're going to drop in a 3-liner "helper" for doing this sort of coding, instead of dropping in inherit() as you show above, why not drop in the conditional polyfill for the already ES5 standardized Object.create()? Surely using standards is better in its own right?

Then, if you're now using Object.create(), it's an easier step to do as I show and drop all the functions and just use the objects. ;-)

I think the proxy pattern vs. Object.create debate belies a more philosophical one, and that is whether to use inheritance or not to. Personally, I like a well-defined inheritance hierarchy. Not everybody does, and that's fine. What I am trying to demonstrate with this inherit function is that "traditional" inheritance is possible and can be straightforward in JavaScript.

...you're just doing objects linked to objects, and the fact that those objects happen to be attached to functions (Parent() and Child()) are really irrelevant.

I can see the appeal of the ES5 Object.create/OLOO approach. As with many things in programming, this comes down to personal preference. I agree that polyfilling Object.create is trivial, but it totally changes how you initialize Objects.

var Parent = { /*..*/ };
var Child = Object.create(Parent);

var child = new Child(); // TypeError: object is not a function

To achieve a similar initialization effect, you would need to do something like:

var Parent = { /*..*/ };
var Child = Object.create(Parent);

Child.init = function () { /*...*/ }

var child = Object.create(Child);
child.init();

I can see how OLOO would be more elegant for static libraries, but some sort of initialization pattern is appropriate in many situations. As far as I am aware, there is no language-standard way of achieving this with Object.create, simply a convention of attaching a function called init. I know a lot of people are anti-new, but I'm not aware of any performance metrics that would indicate that it is indeed unwise to use. Personally I like new and don't really see a problem with it.

I sense that this is edging closer to a debate of inheritance vs mixin, but I feel that that would be outside the scope of this discussion.

@jeremyckahn

...whether to use inheritance or not to. Personally, I like a well-defined inheritance hierarchy.

I don't agree. My point in my previous comment was that you can set up a chain of delegated objects just like (in other languages) you can set up a chain of "inheritance" classes. In JS, you're free to do this chain of delegated objects using Object.create() (which places the emphasis on the objects themselves), or you can do the exact same thing in a more round-about way by having objects attached to functions, and having those objects linked. Your choice, in JS, is which style you like for getting the same end-result. I like simpler.

...it totally changes how you initialize Objects.

Actually, what it comes down to is whether you want to attach your object (whichs hold the stuff you care about) to your constructor, or whether you want to attach your constructor to your object. Really, that's all that's different here. You're attaching your objects to your constructor, I'm attaching my "constructor" (which I can call "init" or whatever) to my object. When you think about it like that, I think you could agree that, conceptually, it makes more sense for a constructor to belong to an object than for an object to belong to a constructor.

Sure, you can "complain" that one code style automatically invokes the function and one doesn't. I don't see the lack of automatic invocation as a big deal. I actually like that I am free NOT to run an initialization automatically if I don't want. You're much less free in the constructor style of code.

...edging closer to a debate of inheritance vs mixin

I don't think this is a discussion of inheritance vs. mixin, I think it's a question of whether the mechanisms that JS has built into it are appropriate to call "inheritance" at all. That's the whole point of parts 1 and 2 of my blog post series, that I'm saying that "inheritance" is totally the wrong word for what you're doing.

You said that your inherit() function is creating inheritance, whereas Object.create() is not doing that. I totally disagree. As I showed above, they accomplish exactly the same thing (ignoring whether you automatically run initialization code in a "constructor" or not -- that's an orthagonal issue).

In both cases, they are creating two objects linked to each other via [[Prototype]]. Now, is this "link" the same thing as the common definition of "inheritance"? I say, plainly, not at all. In fact, it's the opposite.

In traditional definitions of "inheritance" there is (conceptually for sure, and practically, for the most part) a "copy" of the definition from parent class to child class, and from child class to instance. Visually:

By contrast, when you study how [[Prototype]] actually works, what it's really doing is not a copy at all, but instead it's a delegation from instance to child, and from child to parent. Visually:

Notice the important point is that both conceptually, and to a large extent, functionally, "inheritance" means the arrows go left-to-right, top-to-bottom, whereas with "behavior delegation", the arrows go in the opposite direction, from right-to-left, bottom-to-top.

Some people believe this difference is minimal and just two sides of the same coin. I think this is a fundamentally different paradigm. Calling how JS works "inheritance" encourages you to ignore the differences in [[Prototype]] delegation (like the fact that you can, in the middle of the running of your program, re-affect children by changing a parent, which is decidedly UN-inheritance-like). I think that's a dangerous (but all too common) way of approaching JS. Hence, my blog posts were intended to present the different view, which is that we should call it "behavior delegation", and fully embrace what that means.

All that having been said, there are people that do things in JS that are much closer to what inheritance is, and they often call that "mixin", because they actually DO the copy from parent to child. Those who call that "inheritance" are still wrong, IMO, but they're closer to accurate than calling what you're doing "inheritance", because in all ways that matter, what you're doing is delegation.

You can insist that something is the color blue, but if most of the world calls that color green, you're bound to get some confusions and mismatches. :)


Sorry for hijacking your gist. It was not my intention to do so. Thanks for tolerating me.

This is just something that I spent a whole bunch of time thinking about, researching, testing, and writing about, very recently, so it's all very fresh on my mind.

@getify

Thanks for the great explanation. You make a strong case that "inheritance" in a strict sense does not really exist in JavaScript (not counting the contortions that can be done, but that's another story). As I understand it, behavior delegation and inheritance are incompatible concepts, and indeed JavaScript uses the former model.

As far as terminology is concerned, I don't think it's the worst idea to continue to use "inheritance," simply because I think a lot of people are familiar with that concept and it effectively maps to what's being done with [[prototype]] chaining for most cases. Terminology should not preclude a deeper understanding of the mechanics of the language, but people have to start somewhere. Calling it "inheritance" has worked pretty well for the community so far. Trying to force a change in a group's vocabulary can get pretty ugly in any context. ;-)

Another way to look at it: People should be calling Linux "GNU/Linux," but that hasn't happened and we're not really suffering for it.

Generally, I don't fight for a "right" way to write code — just one that works and that people understand. I think that both approaches to defining Object relationships described here are effective, and ultimately it's up to each programmer to use what they are most comfortable with.

I think it matters what we call things, because I think when we mislabel, we set ourselves up for confusion. There's nearly 2 decades of precedent of people misunderstanding how [[Prototype]] works and then blaming the language for its "inheritance" being busted/broken. I think a lot of that misunderstanding would have been avoided if more people had insisted we look at things as "behavior delegation" and eschewed all the OO stuff.

But anyway, great discussion, thanks!

@getify is correct in saying that, if you are going to do this, you should probably use Object.create inside the inherit function since it is standard and (I assume) more efficient.

function inherit (child, parent) {
    child.prototype = Object.create(parent.prototype);
};

however, i have to disagree with dropping new completely in favor of an 'object only' approach. besides new being significantly faster than Object.create in nearly every browser, the syntax for actually intantiating objects is much cleaner with the new operator. Consider your example.

var child = Object.create(Child);
child.init();

vs

var child = new Child();

Maybe its personal preference, but I'd much rather have the verbose part of my code in the object definitions and keep the main application logic clean.

Also consider readability. When using only objects, the only context that I have when reading your code to tell me that it is intended to be used with Object.create is the capitalization of the variable name, and whatever comment you may add.

// looks like any old object literal
var Foo = {
    foo1 : 'abc',
    foo2 : 123
};

If i'm just skimming through the code, I might miss that, or assume its just an important object, not that its intended to be a prototype. But with the normal constructor function approach, we have several clues: function declaration syntax, function name capitalization, use of this inside the constructor function. It's immediately obvious what the intended use is.

// obviously a constructor function
function Foo () {
    this.foo1 = 'abc';
    this.foo2 = 123;
}

Copying from Reddit comment.

I am amazed that these conversations are still occurring, shim Object.create and use this pattern for classes/inheritance :

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create#Classical_inheritance_with_Object.create

ES6 classes are sugar for the above. There is no further discussion needed.

@Offler

This is an extremely short-sighted comment. You're not giving anything new to the discussion that we didn't know 15 years ago. If what you're suggesting was "enough", and there really was "no further discussion needed", then we wouldn't have the vast mountain of incorrect thinking, confusion, etc that has clouded JS "inheritance" for the whole of its lifetime.

The ES6 class keyword is actually going to make the situation way worse. I think it's one of the worst things to happen to the language in the whole of its history.

There's most assuredly more discussion needed, because almost all developers continue to get it wrong in JS, and then blame the language when they try to do something that it's not suited (or intended) to do.

@madbook

besides new being significantly faster than Object.create in nearly every browser

First off, that performance test you link to is flawed and has lots of extra noise to it that makes it hard to see the real point.

Secondly, even if Object.create() is currently slower, that doesn't mean it will stay that way. There's a long history of people making coding choices based not on correctness of thought, but on reasoning about current performance characteristics, then the browsers come along and pave some cowpath and now the original reasoning is totally wrong. There's still a lot of ignorant developers that use Array#join() instead of string concatenation, when it's clearly terribly less efficient. There's dozens of other examples about things that used to be true of performance-based reasoning that are no longer true, and yet people never revisit that once they know it once, so they end up with really badly performing code.

I don't think it's wise to choose one pattern or another simply (or even substantially) based on a performance test. Object.create() is built in to the engine, and will thus be much more optimizable over the long-run. The more people who use it, the more likely the engines will decide to optimize it.

the syntax for actually intantiating objects is much cleaner with the new operator

You've picked the one place where using Object.create() is a little more verbose/ugly than new style coding. There's a mountain of counter-examples where constructor-based coding, in JS, is more verbose and more complex, than objects-only coding.

For example, you list that it's nice that new Fn() automatically executes your constructor, but you don't mention that JS fails to do what C++ does, which is that parent constructors aren't automatically called. So, if you want them called, you have to do awkward "implicit mixin" style coding (or parasitic inheritance) with .call(..) and you have to hard-wire those calls together, because you can't use relative this references to get at the parent constructors ( since they, super confusingly, aren't in the [[Prototype]] chain).

Moreover, when you throw polymorphism into the mix (that is, trying to have two+ levels of your chain with the same method name), you can't do relative polymorphic references to methods, so (just like with constructors) you have to hard-code the connections and use awkard .call(..) jazz, in every single method that needs to make such references up the chain.

Sure, all of that mess is addressable, depending on your stomach for such awkward syntax, BUT importantly you will end up completely circumventing the [[Prototype]] chain to do it (mixins, .call(..), etc). Once you circumvent the [[Prototype]] chain, the question becomes: why did you go to all the "trouble" to wire up this "inheritance" chain when you can't even use it (you have to circumvent it far more often than you get to rely on it).

I wrote a long 3-part article series, the first 2 parts of which are dedicated to pointing out all these problems with thinking about objects in JS with constructors and "traditional OO". I won't repeat that whole set of arguments here, but if you are interested in the counter-argument in its completeness, I invite you to take a read.

If you want a succinct side-by-side comparison of these two coding styles, take a look at this gist. Note that there is more "complexity" in the bottom snippet when it comes to having to manually call init(), but then compare that short-coming to the several places in the top snippet with more "complexity", like referencing .prototype properties, having to add everything to the .prototype, using .call(this), etc.

This is especially true when you compare the reflection tests (the end of each snippet), which complicates significantly in the usage code the process of inspecting and reasoning about the "type" of object your reference points to.

My point is that the tradeoff of having to manually call init() buys you significantly cleaner code in a lot of other ways.

@getify

Honestly, I'm a bit afraid to jump into a discussion of this magnitude of fear to be booed out (also, my English is far from perfect), but here goes.

You've made a lot of excellent points about the use of Object.create and how it might make the code simpler. With regards to the class keyword they are to introduce in ES6, I must say I have mixed feelings. Part of me feels like throwing more confusion into the mix will only increase the frustration, however I can also see how it would help a lot of people having little to no experience in javascript to express what they want to do in a familiar way.

Which brings me to my main point; you see, one of the problems with programming in general, is that there is no way at all you're going to learn everything. People have limited resources with regards to learning programming, and the fact that a person with almost no programming experience at all can browse through the jQuery-documentation (because, let's face it, a lot of javascript is still about basic DOM-manipulation) and figure out how to make his button fade out is one of the beauties of javascript. And one of the reason why I think javascript has become so popular is the fact that you can get up an running in no time flat. You don't need to know how prototypes works in order to fill the needs of the basic programming-tasks.

I work regularly with a person who does web-programming 40 hours a week, client and server (only the client is in javascript). Their entire web-system uses ajax for saving, and there are a lot of dynamic features. His experience is that of VBS, and he does not know how a javascript-object works at all. Tail-chaining of functions, lambdas, prototypes, callbacks, none of these does he understand how works, yet that does not hinder him in doing his job. And he does it well too. Sure, a lot of the code could be written better, and some DOM-lookups could have been saved, not to mention the fact that if you want to store a list of database-posts you could do it in a js-variable instead of writing it out to a html-table, but everything still works as it's supposed to do. And it can only do that because javascript is made in such a way that nomatter how little about the underlaying you actually understand, you are still able to express a fair share of programming-intent, and javascript will understand it.

And no matter what coding-style you have, javascript can sort of do that too. I mean seriously: You come from programming-languages using only simple variables (int/string/array), and global procedures? Sure, javascript can do that. You don't want to create anything public at all, everything should be hidden away in closures? Sure, no problem. You're used to classes and inheritance like in java? That too can be handled. Sure, several of these (if not all) have some problems, but it's enough to get you started. To get you learning.

And neither of these should be considered wrong. They are simply options.

Well, at least that's what I think.

@Alxandr

First off, thanks for your thoughts. I think they are welcomed in the conversation.

You make a good point that one of JS's strengths is that you can "get the job done"™ without always understanding the underlying mechanisms to any great detail. It's certainly made JS a much more approachable language for the "masses" than many other languages.

Of course, the perspective that I'm trying to bring to light here in this thread, and in my blog posts, and in the books I'm about to write, is that there IS a way to learn and understand JS at a deeper level and that in doing so, you will increase your ability to write high quality, performant, and maintainable code.

It's a level of maturity of coding styles that I think we should encourage all JS developers to strive toward, but knowing that (unfortunately) many of them will not care nor have the impetus to do so. The more that do, though, the better the overall community will be, I believe.

With particular respect to me suggesting a simpler way of tapping into [[Prototype]] by looking at it as "behavior delegation" and not as "inheritance" (with all the baggage and confusion that brings in JS), I offer these two quotes:

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. --Antoine de Saint-Exupery

Any fool can make something complicated. It takes a genius to make it simple. --Woody Guthrie

For me, simpler is almost always better. Anyway, thanks for your input. :)

@Alxandr

however I can also see how [ES6 classes] would help a lot of people having little to no experience in javascript to express what they want to do in a familiar way.

Sure, it's going to smoothe over some of the awkward .prototype syntax, but since it's only syntax sugar and not going to change the underlying mechanism in any way, it isn't going to fix any of the pitfalls of how so called "prototype inheritance" is different from "traditional inheritance".

For instance, you're still going to be able to shoot yourself in the foot, accidentally and too easily, by changing something in the parent level of your chain, and unlike all other traditional class-based languages you're going to confusingly affect your children. The metaphor is: "Sure, my son inherited many of my genetic traits, but when I break my leg, he shouldn't have his broken too!"

This, and several others, are long-time gotchas in the language around the objects system, and almost none of the writing and education over the last nearly 20 years has done much to address that confusion for new (and even mildly seasoned) JS devs. That tells me we're doing something wrong, and as the old adage goes, "only a fool keeps doing the same thing over and over expecting a different result".

So, now, ES6 is going to come along and make it even easier to create these relationships, and at the same time give them an even more "traditional" label (by calling it class { .. }). That is almost certainly going to make it even easier for new and mildly seasoned JS devs to stumble down the path with incomplete understanding, and when they run into those footguns, they're going to have even more ammunition to blame the language.

Taking a step back, and embracing what JS is and what it's not, gives us a chance to reset our thinking enough to possibly have a chance to avoid those footguns. At least, that's what I am trying to do. :)

@getify

Yes true that was a brusque reply it's just frustrating to read yet another reddit thread about inheritance in JS as if it's some amazing new discovery.

I think class is excellent sugar, you seem to be worried about people accidentally changing super classes and that affecting the child classes - I must say I've never come across such bad code in my life. Dynamically modifying class structure at runtime is such an obviously bad thing to do I think the issue is far less important then you may think.

If that's the only issue you can think of that could cause problems then I think most of us can sleep soundly at night.

@Offler

Well, I've mentioned several problems (that just being one of them) here, and I wrote a 3-part blog post series about it recently, too, laying out my case: http://davidwalsh.name/javascript-objects

It's probably not prudent or useful for me to re-write all that in this thread, so if you are at all interested in the counter-argument against constructor-based coding, you might take some time to read them. But I assure it's much more than just one little argument.

I'm glad you've never seen code which confuses the fact that changes to a parent affect children, but I assure you, it's a long established footgun in JS that's tripped up scores and scores of developers. I can't even count how many blog posts and books I've read which get it wrong, too. Just because you don't struggle with that issue by no means proves that it isn't an issue for the greater JS community.

@offler it's not all about changing the actual classes at run time. You can write container objects that can take an instance and use that instance as a prototype, effectively creating a new "enhanced" object that you can pass along and use as if it was the old one but with extra functionality while still also retaining the old object.

I use this approach to create filtered collections in backbone where I can create a filtered collection that can be used the same as whatever is passed in (including the extra functionality that was put on top of that collection).

What you're really doing in this case is you want to delegate the behaviour up the chain rather than inherit it (though there can be issues with "this").

Here's a couple of the gotchas where "prototypal inheritance" can be confusingly different (to a seasoned classical inheritance dev):

function Dad(){}
Dad.prototype.limbs = {
   leftLeg: true,
   rightLeg: true,
   leftArm: true,
   rightArm: true
};
Dad.prototype.break = function(limb) { this.limbs[limb] = false; };

function Son(){}
Son.prototype = Object.create(Dad.prototype);

var mydad = new Dad();
var me = new Son();

mydad.break("rightLeg");
me.limbs.rightLeg; // false! my dad broke his leg so my leg is broken?!?

In this example, the gotcha is that as a classicist you'd be expecting that the properties in the parent Dad class would be "copied" to the child Son class at inheritance time, so that mydad and me were not sharing the same limbs property. But in JS, a property is "shared" (via delegation) unless you manually make a copy, or just never assign data properties to anything but the instance (the this).

Of course, once you understand this gotcha, you can avoid it, but it's one of those weird things that trips up a lot of devs when they first come to JS's version of OO.

Another gotcha (of any object prototypes, regardless of classes, honestly), this time in reverse:

function Parent(){}
Parent.prototype.something = 42;

var p = new Parent();
p.hasOwnProperty("something"); // false

p.something--; // apparently the same as `p.something = p.something - 1`
p.hasOwnProperty("something"); // true!!

Most developers from other languages aren't terribly familiar with the concept of LHS vs RHS as it relates to variable references. The expression p.something = p.something - 1 has both going on, subtlely. The right-hand p.something looks up the current property, and finds it via delegation up on the Parent.prototype object (not on the p object).

But then the left-hand p.something is an assignment, and assignment follows a different process than property lookup. It assigns a new property on the owned object (the p) if one didn't already exist there, so instead of changing Parent.prototype.something, it creates a new p.something.

Furthermore, the usage here of the -- decrement operator is usually seen as being an "in-place" operator, since it doesn't have an explicit assignment in it. But in reality, under the covers, there is an assignment back to the property, which opts you into that confusing and not-terribly-obvious property shadowing.

You can closely analyze these behaviors and understand perfectly what's going on here. But most JS devs don't fully understand those processes, and especially developers coming from other languages, this sort of thing can look really strange. It's definitely the source of much "blame" placed on the design of the language.

BTW, earlier in this thread (/cc @madbook), the complaint was made that constructors not being called was a pain of the OLOO style of code. I agree. I've come up with a decent way to address that concern, IMO:

var Foo = {
    Foo: function(who) {
        this.me = who;
        return this;
    },
    identify: function() {
        return "I am " + this.me;
    }
};

var Bar = Object.create(Foo);

Bar.Bar = function(who) {
    // "constructors" (aka "initializers") are now in the `[[Prototype]]` chain,
    // so `this.Foo(..)` works easily w/o any problems of relative-polymorphism
    // or .call(this,..) awkwardness of the implicit "mixin" pattern
    this.Foo("Bar:" + who);
    return this;
};

Bar.speak = function() {
    alert("Hello, " + this.identify() + ".");
};

var b1 = Object.create(Bar).Bar("b1");
var b2 = Object.create(Bar).Bar("b2");

b1.speak(); // alerts: "Hello, I am Bar:b1."
b2.speak(); // alerts: "Hello, I am Bar:b2."

My convention here is that I don't call my constructor/initializer "init", but instead name it the same as the object it belongs to (sorta like real OO constructors, right?). By having these functions on the objects themselves, rather than having the objects belong to the functions, now the parent "constructors" are on the [[Prototype]] chain, and can easily be accessed without polymorphic problems using this style references, as shown. I also allow the "constructors" to be chain-called since they explicitly return this.

That lets us create the object, and initialize it, in one line: var b1 = Object.create(Bar).Bar("b1");. No, it's not as graceful as the attractiveness of automatically new-called constructors, but it's not that bad, and as I've asserted above, OLOO solves a lot of other ugliness of constructor style code patterns.

@getify
I think your example on the broken legs are a terribly faulty one. If you create an instance of an object, said object is passed by reference, and there will only ever be one of the object no matter how many instances of the reference you hold. This is true in a lot of languages, only primitives are passed by value. So no matter where you're from, it should come as no surprise that me.limbs is exactly the same as mydad.limbs. You also seem to forget the fact that while you speak a lot of delegation, there's actually a lot of copying happening behind the scenes. When you access a property on the prototype it is actually copied into the instance. For instance, take a look at the following (rewritten) example:

function Dad(){}
Dad.prototype.brokenLeg = false;
Dad.prototype.break = function() { this.brokenLeg = true; };

function Son(){}
Son.prototype = Object.create(Dad.prototype);

var mydad = new Dad();
var me = new Son();

console.log('my leg is broken: ' + me.brokenLeg); // no legs broken ofcause
console.log('dads leg is broken: ' + mydad.brokenLeg); // no legs broken ofcause
mydad.break();
console.log('my leg is broken: ' + me.brokenLeg); // still no legs broken
console.log('dads leg is broken: ' + mydad.brokenLeg); // dad's leg is broken

If you remove the first check on me.brokenLeg though, you get true instead of false. The point here being that the prototype-chain (if you want to use it for sorta regular OOP) should maintain functions and other read-only values. If you want to have instance-spesific values, you assign them to this in the constructor.

Oh, and on your quote on simplicity. Simple does not necessarily mean not complex or in lack of options, sometimes it simply means easy to use. Sometimes bloated with options is the simpler alternative becuase people will recognice things (that may or may not be similar to what they think it is), and it's a place to start for them.

@Alxandr

Sorry but it's your post that is "terribly faulty". Let me address several things:

If you create an instance of an object, said object is passed by reference, and there will only ever be one of the object no matter how many instances of the reference you hold. This is true in a lot of languages, only primitives are passed by value. So no matter where you're from, it should come as no surprise...

Either you have no experience (that you recall the details of) with true OO languages, or you completely missed the fact that my point was how the prototype system in JS is strange compared to those true OO languages, not strange compared to other prototype systems. I'm frankly not sure which is causing you to get this wrong, but you did miss the point, nonetheless.

In true OO languages, if I declare some protected member properties in a parent class, and then a child class inherits from that parent class, when you instantiate that child class, each child instance will get its own copy of those member properties. AFAIK, in those languages, the only way to create "shared state" is to actually make static properties on a class. You can't accidentally do so in those languages.

Because of that fact, if you're from one of those languages, you're at least somewhat likely to first try to put some properties into a parent "class" (aka Dad) and thus assume that when your child "class" (aka Son) "inherits" from it, the child class (and indeed, all child instances) will get its own copy of those members.

Now, I obviously know that this is faulty reasoning (ie, it won't work that way in JS), and that only member methods and shared member properties should go on the parent prototype, whereas non-shared member properties must always go on a this instance. It's a learnable fact, and once you do, JS's mechanism is tenable to use.

But a new JS dev coming from a traditional class-oriented language is highly likely to miss (or be confused by) this difference, and many many many have. The proof is in google and stackoverflow history for the last decade+. A newbie JS dev writing my above snippet is doing so in good faith but is nonetheless going to trip over the problem quickly.

That's exactly what I meant in my previous post when I said:

Here's a couple of the gotchas where "prototypal inheritance" can be confusingly different (to a seasoned classical inheritance dev)

I suppose you missed that intended meaning?

When you access a property on the prototype it is actually copied into the instance.

Ummm, no, that's not how JS works. Accessing a property via [[Prototype]] doesn't do any copying... if you try to set a property, then it will be set directly on the instance, which can look like copying, but it's not really copying, it's called "shadowing". In either case, it only happens when you try to set, not when you try to access.

If you remove the first check on me.brokenLeg though, you get true instead of false.

Ummm, no. Try it again. The console.log('my leg is broken: ' + me.brokenLeg); // no legs broken ofcause has no impact on the how console.log('my leg is broken: ' + me.brokenLeg); works.

In fact, your code snippet is quite subtlely confused. What does the copying of brokenLeg property is the break() method, because it says this.brokenLeg = .... When you call mydad.break(), mydad.brokenLeg now exists, where before it didn't exist and was instead delegating to mydad.__proto__.brokenLeg (aka Dad.prototype.brokenLeg). When mydad.brokenLeg property is created, he's now "shadowing" mydad.__proto__.brokenLeg.

So, after the call to break(), now you've switched to where you're confusingly comparing a shadowed owned property mydad.brokenLeg (which has been specifically set to true) with a delegated me.brokenLeg which of course is actually still delegating to me.__proto__.__proto__.brokenLeg (aka Dad.prototype.brokenLeg).

FWIW, that's the opposite of what you claimed about me.brokenLeg.

With all due respect, it appears your misunderstandings on this topic are somewhat of supporting proof of my overall point, that this stuff isn't terribly self-obvious and confuses lots (maybe most?) devs.

If you want to have instance-spesific values, you assign them to this in the constructor.

Yeah, duh.

But that wasn't the point of my previous code snippet. Again, to repeat myself, the point of my code was that a newbie JS dev would assume that, like in their previous real-OO language, those members on the parent class would end up automatically instance-specific.

It's not until later, when they run into all these gotchas, and learn the hard way, that they "get" it. So thanks again for pointing out what we all (or most of us, anyway) already knew but failing to address what someone new expects when they first arrive at JS and hear about "classes" and think they work like classes work in traditional OO languages.

BTW, there are definitely uses for shared properties on the Dad.prototype. For example:

function Dad(first){ this.firstName = first; }
Dad.prototype.lastName = "Baker";
Dad.prototype.name = function() { return this.firstName + " " + this.lastName; };

function Son(first){ this.firstName = first; }
Son.prototype = Object.create(Dad.prototype);

var mydad = new Dad("James");
var me = new Son("Frankie");

console.log("My dad's name: " + mydad.name()); // "My dad's name: James Baker"
console.log("My name: " + me.name()); // "My name: Frankie Baker"

In this case, it's clear that neither Son.prototype nor me objects need a copy of Dad.prototype.name, because it's perfectly sensible to delegate up the [[Prototype]] chain for lastName. Side note: of course, once a theoretical Daughter instance like mysister gets married, she's then gonna need her own copy of lastName so it can change if she wants, but until then, it's fine to delegate as another of mydad's children.

Simple does not necessarily mean not complex or in lack of options, sometimes it simply means easy to use

This is an extremely common claim but is nevertheless misguided. Rather that re-count the explanation for the super important difference between simplicity/complexity and ease/difficulty, I'll just point you at the definitive argument: Rich Hickey's "Simple Made Easy" talk. No sarcasm. Seriously, go watch it. It was a ground-breaking, transformative talk when I heard it at Strange Loop a couple of years back.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.