JS Objects: De”construct”ion
JS Objects: TL;DR
JavaScript has been plagued since the beginning with misunderstanding and awkwardness around its "prototypal inheritance" system, mostly due to the fact that "inheritance" isn't how JS works at all, and trying to do that only leads to gotchas and confusions that we have to pave over with user-land helper libs. Instead, embracing that JS has "behavior delegation" (merely delegation links between objects) fits naturally with how JS syntax works, which creates more sensible code without the need of helpers.
When you set aside distractions like mixins, polymorphism, composition, classes, constructors, and instances, and only focus on the objects that link to each other, you gain a powerful tool in behavior delegation that is easier to write, reason about, explain, and code-maintain. Simpler is better. JS is "objects-only" (OO). Leave the classes to those other languages!
Due Thanks
I'd like to thank the following amazing devs for their generous time in feedback/tech review of this article series: David Bruant, Hugh Wood, Mark Trostler, and Mark McDonnell. I am also honored that David Walsh wanted to publish these articles on his fantastic blog.
Complete Series
In part 1 of this article series (which you should totally go read if you haven't yet!), I revisited an idea not original to me: JS doesn't have "inheritance" in the traditional sense, and what it does have is more appropriately labeled "behavior delegation" -- the ability of one object to delegate a method or property access which it cannot handle over to another object which can handle it.
Then, in part 2, I addressed several distractions which I think obfuscate JS's true object-oriented identity, including "custom types", "mixins", "polymorphism" (which we'll come back to again later), and even the new "class syntax" coming in ES6. I suggested that to understand (and leverage) better the [[Prototype]]
, we needed to strip away the cruft. Here, I will attempt to do that.
Turtles Objects all the way down up
The key realization, the punchline to this entire article series, is that [[Prototype]]
is really only about linking one object to another object, for the purposes of delegating, if the first object cannot handle a property or method access but the second can. In other words, it's only objects, linked to other objects. That's really all JS has.
In a sense, JS is the purest essence of a "object-oriented (OO)" language, in that it really is all about objects. In contrast to most other languages, JS is somewhat unique that you can actually create objects directly without the notion of classes or other abstractions. That's a powerful and brilliant feature!
People often bash JavaScript, but it's one of the few prog languages that let you directly create objects. Others: Lua, Dylan, Cecil.
— Axel Rauschmayer (@rauschma) April 9, 2013
JavaScript legitimately is "object-oriented", and perhaps we shouldn't have used that term for the other languages which imply a lot more than just "objects". Maybe "class-oriented" would have been more accurate, which would have freed us up to use "object-oriented" for JS. Of course, as I argued in part 1, what everybody means when they use some term, matters, so it's far too late to redefine or bend the commonly accepted "object-oriented" to my own purposes, much as I'd like to.
I'm mildly tempted, however, to just hijack the abbreviation of "OO" to mean "objects-only" instead of "object-oriented", but I bet that probably wouldn't get anywhere, either. So, for our purposes here, let's just sayJavaScript is "object-based (OB)" to clarify against "object-oriented (OO)".
Whatever we call it, we normally tap into this object mechanism by following the "OO way": we create a function which we use as a "constructor", and we call that function with new
so that we can "instantiate" our "class", which we specify with the constructor function together with its subsequent .prototype
additions... but all that is like a magician's sleight of hand that dazzles you over here to distract you from what's really going on over there.
What really matters, at the end of the trick, is that two objects end up linked to each other via the[[Prototype]]
chain.
Codez Plz
Before we can derive and understand that simpler view of "objects-only" or "object-based", we need to understand what actually gets created and linked when we build up some "inherited" objects in JavaScript. Not only are we going to see what happens by default, but what doesn't happen.
Take this code for our main example:
function Foo(who) { this.me = who; } Foo.prototype.identify = function() { return "I am " + this.me; }; function Bar(who) { Foo.call(this,who); } Bar.prototype = Object.create(Foo.prototype); // NOTE: .constructor is borked here, need to fix Bar.prototype.speak = function() { alert("Hello, " + this.identify() + "."); }; var b1 = new Bar("b1"); var b2 = new Bar("b2"); b1.speak(); // alerts: "Hello, I am b1." b2.speak(); // alerts: "Hello, I am b2."
Note: Some people write Bar.prototype = Object.create(Foo.prototype);
as Bar.prototype = new Foo();
. Both approaches end up with the same linked objects, where Bar.prototype
is an object linked via its[[Prototype]]
to Foo.prototype
. The only real difference is whether or not the Foo
function is called during the creation of Bar.prototype
. Depending on your circumstances and intent, you may or may not want that to happen, so let's consider them roughly interchangable but with different purposes.
What we have is an object labeled Foo.prototype
with an identify()
method, and another object calledBar.prototype
with a speak()
method. Bar.prototype
is a new empty object that is [[Prototype]]
-linked to Foo.prototype
. Then we have two objects b1
and b2
, who each are each respectively linked via their own [[Prototype]]
to Bar.prototype
. b1
and b2
also have an "owned property" directly on each of them called me
, which respectively holds the values "b1" and "b2".
Let's take a visual look at the relationships implied by the above code snippet:
Note: All the [[Prototype]]
links in the diagram also mention a ".__proto__" property. __proto__
is a formerly non-standard property (which exists in most but not all JS environments) to expose the internal [[Prototype]]
chain. As of ES6, however, it will be standardized.
I left a whole bunch of detail out of that diagram, intentionally, so it was even remotely digestable. But of course, since JS is all objects, all the linkages and ancestry of each item can be fully traced. We'll come back to all the omitted parts of this diagram in a little bit.
Note in this diagram that the function constructors all have a .prototype
property pointing at an object. As we've been suggesting, the object is what we really care about, and in this way of viewing the JS object mechanism, the way we get that object is to look at a constructor function's .prototype
. The function doesn't really serve any particularly important role.
I know a whole bunch of you just screamed out, "sure it does! it runs constructor code to initialize the new object!" OK, you're technically right. Foo()
has some code in it which is ultimately run against b1
and b2
.
But the devil's always in the details. First, we don't need a constructor function to run such code. That's just one way of getting that outcome. And I'm going to suggest it's a more distracting approach.
Secondly, unlike C++, the base-class/superclass Foo()
"constructor" doesn't automatically get called when you run the child-class Bar()
"constructor" to make b1
and b2
. So, like Java, we have to manually call theFoo()
function from Bar()
, but unlike Java, we have to do so with a variation of the explicit "mixin" pattern (I'd probably call it "implicit mixin" here) to make it work like we expect. That's an ugly detail that is very easy to forget or get wrong.
So, where you'd probably argue with me that "constructor" functions are useful being automatically called at the construction of an object, I'd point out that this is true for only the immediate level, not for the entire "chain of inheritance", which means that automatic behavior is pretty limited/shallow in utility.
Polymorphism redux
Moreover, we see here the first hint of the problems with relative polymorphism in JS: you can't do it! I can't tellBar()
to automatically and relatively call his ancestor constructor(s), via a relative reference. I have to manually call (aka, "borrow") the Foo()
function (it's not a constructor here, just a normal function call!) from inside ofBar()
, and to make sure the this
is bound correctly, I have to do the slightly more awkward .call(this)
style of code. Ugh.
What may not be obvious until you go back and look closer at the diagram above is that the Foo()
function isnot related in any useful/practical way to the Bar()
function. The Foo()
function does not even appear in the "inheritance" (aka "delegation") chain of Bar.prototype
object. The fact that there are some lines you can follow on the graph for indirect relationships doesn't mean that those relationships are what you'd want to rely on in your code.
The problem with polymorphism we're seeing here is not only for "constructor" functions. Any function at one level of the [[Prototype]]
chain that wants to call up to an ancestor with the same name must do so via this manual implicit mixin approach just like we did inside of Bar()
above. We have no effective way of making relative references up the chain.
Importantly, this means that not only do we establish the link between Bar
and Foo
once at "class" definition, but every single polymorphic reference also has to be hardcoded with the direct relationship. This significantly decreases the flexibility and maintainability of your code. As soon as you make a function hard-coded with implicit mixin to an "ancestor", now your function can't be "borrowed" as easily by other objects without those possible unintended side effects.
OK, so let's say you agree with me at this point that polymoprhism in JS is more trouble than it's worth. Using constructor-based coding to wire JS objects to each other forces you into the problems of polymorphism.
.constructor
Another detail that's easy to miss is that an object's .constructor
property really doesn't behave like we'd probably expect. It's correct at the Foo()
level of the graph, but below that, at Bar()
and b1
/ b2
, notice that the implied linkage there shows .constructor
references, strangely, still pointing at Foo
.
Actually, what this means is that the only time a .constructor
property is added to an object is when that object is the default .prototype
attached to a declared function, as is the case of Foo()
. When objects are created via new Fn()
or Object.create(..)
calls, those objects don't get a .constructor
added to them.
Let me say that again: an object created by a constructor doesn't actually get a .constructor
property to point to which constructor it was created by. This is an extremely common misconception.
So if you reference b1.constructor
for instance, then you're actually going to delegate up the chain a few links, to Foo.prototype
. Of course, Foo.prototype
has a .constructor
property and it's pointing at Foo
like you'd expect.
What's that mean? In the above snippet, right after you perform Bar.prototype = Object.create(Foo)
(or even if you'd done Bar.prototype = new Foo()
), if you plan to rely on the .constructor
property (which many do), you need to perform an extra step, right where I put the JS "Note:" comment:
//... Bar.prototype = Object.create(Foo.prototype); Bar.prototype.constructor = Bar; // <-- add this line! //...
Then b1.constructor
references will delegate to that Bar.prototype
level, and will "correctly" point at Bar()
as you'd probably have expected. Ugh...**more syntax gotchas** that user-land libs always have to "fix" for us.
Furthermore, the fact that Foo.prototype
has a .constructor
property pointing at Foo
is strange, when you think about "constructor" the way most people do. It's nice that it gives objects created by new Foo()
a way to delegate to a .constructor
property access and find Foo()
, but it's bizarre where .constructor
actually lives.
It implies that Foo()
constructed Foo.prototype
, but that's nonsense. Foo()
had nothing to do with creating the default Foo.prototype
. Foo.prototype
defaults to an empty object that was actually constructed by the built-in Object()
constructor.
So we have to change how we think of what the .constructor
property means. It does not mean "the constructor this object was created by". It actually means "the constructor which creates any objects that end up getting [[Prototype]]
linked to this object." Subtle but super important difference to get straight.
Point? These confusions only happen/matter if you're using constructor-style code, so it's the choice of this style of code that opts you into the problems. You don't have to live with that pain. There's a better, simpler way!
The Whole Pie
Now let's look at everything that's actually implied by the above snippet of code. Ready for the whole messy thing?
Take a few minutes to just take all that in. Why show you such a complex diagram?
This diagram actually shows you where some of JavaScript's functionality comes from, where before you might have just never considered how it all worked. For instance, have you wondered how all functions are able to use behavior such as call()
, apply()
, bind()
, etc? You may have assumed each function has that behavior built-in, but as you can see from this diagram, functions delegate up their [[Prototype]]
chain to handle those behaviors.
While the behavior delegation part is sensible and useful, consider all the implied complexity of constructor-style coding as visualized here. It's pretty tough to trace all the different entities and diagrams and make much sense of it all. A lot of that complexity comes from the function constructors. (here's the same complete graph but with the implied relationship lines omitted, if that helps to digest)
If you take that diagram, and remove all the functions and any associated arrows (which we'll see in just a moment), you're left with "objects-only", and you'll have a much more simplified view of the JS objects world.
Simpler: Object -> Object
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. --Antoine de Saint-Exupery
For refresher, the same prototype-style code from above:
function Foo(who) { this.me = who; } Foo.prototype.identify = function() { return "I am " + this.me; }; function Bar(who) { Foo.call(this,who); } Bar.prototype = Object.create(Foo.prototype); // NOTE: .constructor is borked here, need to fix Bar.prototype.speak = function() { alert("Hello, " + this.identify() + "."); }; var b1 = new Bar("b1"); var b2 = new Bar("b2"); b1.speak(); // alerts: "Hello, I am b1." b2.speak(); // alerts: "Hello, I am b2."
Now, let's instead consider this alternative snippet of code, which accomplishes exactly the same, but it does so without any of the confusion/distraction of "constructor functions", new
, .prototype
, etc. It just creates several objects and links them together.
var Foo = { init: function(who) { this.me = who; }, identify: function() { return "I am " + this.me; } }; var Bar = Object.create(Foo); Bar.speak = function() { alert("Hello, " + this.identify() + "."); }; var b1 = Object.create(Bar); b1.init("b1"); var b2 = Object.create(Bar); b2.init("b2"); b1.speak(); // alerts: "Hello, I am b1." b2.speak(); // alerts: "Hello, I am b2."
Let's try to take some comparison looks between this snippet and the previous one. They both accomplish the same thing, but there's some important differences in how we get there.
First of all, Bar
and Foo
are now just objects, they're not functions or constructors anymore. I left them as capital letters just for the symmetry and because some people feel better with them. They make it clear that the objects being linked are what we cared about all along, so instead of the indirectness of linking Bar.prototype
toFoo.prototype
, we just make Foo
and Bar
objects themselves and link them. AND, we only need one line of code to link them, instead of the extra ugly polymorphic linkage. Bam!
Instead of calling function constructors like new Bar(..)
, we use Object.create(..)
, which is an ES5 helper that allows us to create a new object and optionally provide another object to [[Prototype]]
link it to. We get the same outcome (object creation and linkage) as a constructor call, but without needing the constructor. BTW, there's a simple non-ES5 polyfill for Object.create(..)
, so you can safely use this style of code in all browsers without concern.
Secondly, notice that because we're not worried about constructors anymore, we have eliminated any concerns about awkward polymorphisms forcing us to do manual implied mixins to call Foo()
from Bar()
. Instead, we put the code we wanted to run to initialize our objects into a init()
method, on Foo
, and we can now callb1.init(..)
directly via the delegation chain and it "magically" just works like we want.
So, we have a tradeoff here. We don't get automatic constructor calls, which means we create the object likevar b1 = Object.create(Bar)
and then we have to additionally call b1.init("b1")
. That's "more code".
But the benefits we get, which I think are much better and well worth it, are no awkwardness with the linkage between Foo
and Bar
-- instead we leverage [[Prototype]]
delegation to get at the code reuse ininit()
. Also, no more verbose/repetitive .prototype
references, and neither do we need to use.call(this)
nearly as often (especially if we avoid polymorphism!).
Looks are everything
And to visualize the simplicity this approach brings us, here's the diagram when we remove the functions entirely and focus only on the objects:
I don't know about you, but I just think that mental model is so much cleaner, and the bonus is that its semantics perfectly match the code.
I have shown you simple enough code using only core JS syntax, that I don't need any helper libraries to wire up my objects. Of course, I could use one, but why? Simpler is better. KISS.
Any fool can make something complicated. It takes a genius to make it simple. --Woody Guthrie
For the record, I'm not even remotely the genius here. Brendan Eich, creator of our language, was the genius for making something so powerful yet so simple.
Object self-reflection
Last thing to address: how does this simplification affect the process of reflecting on an object? In other words, can we inspect an object and find out about its relationships to other objects?
For prototype-style code, reflection looks like this:
b1 instanceof Bar; // true b2 instanceof Bar; // true b1 instanceof Foo; // true b2 instanceof Foo; // true Bar.prototype instanceof Foo; // true Object.getPrototypeOf(b1) === Bar.prototype; // true Object.getPrototypeOf(b2) === Bar.prototype; // true Object.getPrototypeOf(Bar.prototype) === Foo.prototype; // true
Notice that you're using instanceof
and having to think in terms of the constructor functions that made your objects, and their .prototype
s, rather than just reflecting on the objects themselves. Each of those reflections comes with slightly more mental tax as a result.
And when there's only objects?
Bar.isPrototypeOf(b1); // true Bar.isPrototypeOf(b2); // true Foo.isPrototypeOf(b1); // true Foo.isPrototypeOf(b2); // true Foo.isPrototypeOf(Bar); // true Object.getPrototypeOf(b1) === Bar; // true Object.getPrototypeOf(b2) === Bar; // true Object.getPrototypeOf(Bar) === Foo; // true
By contrast, reflection on objects is only about the objects. There's no awkward references to a constructor's.prototype
property for the checks. You can just inspect if one object is related via [[Prototype]]
to another object. Same capabilities as above, but with less mental tax.
Moreover, as I mentioned in part 2, this sort of explicit object reflection is preferable and more robust/reliable than implicit detection through duck typing.
Object.wrapItUpAlready()
Take a deep breath! That was a lot to take in. If you've followed all 3 parts of the article series, I hope by now you see the bottom line: JS has objects and when we link them, we get powerful behavior delegation.
There's just no need to pile on class-orientation on top of such a great system, because it ultimately just leads to the confusion and distraction that has kept JS' object mechanism shrouded and covered up by all these helper libraries and misunderstandings about JS syntax.
If you stop thinking about inheritance, and instead think with the arrows headed the other way: delegation, your JS code will be simpler. Remember: it's just objects linked to objects!
About Kyle Simpson
Kyle Simpson is a web-oriented software engineer, widely acclaimed for his "You Don't Know JS" book series and nearly 1M hours viewed of his online courses. Kyle's superpower is asking better questions, who deeply believes in maximally using the minimally-necessary tools for any task. As a "human-centric technologist", he's passionate about bringing humans and technology together, evolving engineering organizations towards solving the right problems, in simpler ways. Kyle will always fight for the people behind the pixels.
Nice article! But most of the time I’m using the “init/constructor” function on the Bar object to run additional initialization code (in addition to the Foo initialization code), and that functionality is not available in the “Object -> Object” example. Code re-use through polymorphism is difficult in JavaScript without using any tricks or hard-linking the Foo and Bar objects. I hope ES6 will solve this.
Yes, very important point you’ve made.
If you need multi-level initialization, you have the unfortunate choice that you either use the same name
init()
at each level, and then you deal with the frustrations of hard-coded polymorphic references, OR you can give each init task a unique name, which is often a better idea anyway, and if you do that, thenthis.fn()
style calls will work safely (and relatively) through the[[Prototype]]
delegation chain as you’d want.For instance, say you had one set of initialization at
Foo
level which set the identity, and another set of initialization at theBar
level which customized some output preferences:It’s better software practice to use specific task names rather than try to “overload” a generic name like “init”, so this sort of design is not only more maintainable, it happens to fit very well with the style of code I’ve presented here.
Hopefully that helps! :)
@Kyle you don’t have to hard-code the polymorphic reference:
it looks a bit ugly with the double calling getPrototypeOf, but it works, and is generic from this point. one gotcha is, that oyu have to check for the existence of the init method, at the end of the chain there is no such method anymore.
@Gregor-
Unfortunately, that’s a very dangerous approach. It only works when you have 2 levels of the chain. If you ever had a third level, you’ll have to change all such occurrences with an extra getPrototypeOf call, and a fourth level later will mean yet another call in all those places. It’s a refactoring footgun of the worst variety.
I Should really check my code first:
realised it wouldn’t work for the second super call, so here is one that should work based off of goog.base:
@rhys I think your final code snippet makes my point for me. It is POSSIBLE to traverse the prototype chain each time you want to make a
super
call… that’s what mycurrentThis
polyfill did. But it’s terribly ugly and inefficient, so much so that I’d argue it’s impractical to use such techniques. It’s exactly this kind of ugliness that I was referring to in several places when I condemned polymorphism (the relative kind) in JS.On a side note, you could have done:
Technically, THAT is a relative sort of super call… where at least it isn’t hard coding Bar to Foo. However, there are gotchas with this approach too, and there is unquestionably a lot of ugliness to that sort of coding. That style of code is highly likely to be done wrongly, and even if it’s right, it’s harder to understand/maintain later. All of those are points I made throughout the article series on why that style of code is something I think that makes JS
[[Prototype]]
harder to use/understand. It’s not just a lack of syntactic sugar, it’s that fundamentally we don’t have acurrentThis
orsuper
, and our workarounds for that are, IMO, uglier than polymorphism is worth.I posted another comment here on this article where I suggested another way to deal with multiple-levels of initialization without relying on polymorphism. That’s the style of code that I think is WAY better to write. Just my opinion, though.
Is this mad? https://jsbin.com/kopogu/2/edit?js,console
Thanks for the article. Could you recommend more material to study that approaches the language in the manner you do? My eventual project work will be for multi-player (local, 100 or fewer users) game and info-viz application with heavy use of MySQL data. Thanks again.
At the risk of sounding too glib, I have a blog I plan to write more on soon, and I’m also planning to launch a kickstarter in the next few days for a new book series I want to write on JS, so I think you should read more of my stuff! ;-) (keep an eye out here for announcements for my kickstarter)
But, seriously, Angus Croll and I seem to have a lot of mindset about JS in common, and he writes some great stuff on his blog. Hope maybe that helps for now.
FYI, I have indeed launched my Kickstarter for the book series “You Don’t Know JS”. Check it out and help spread the word!
Oops, sorry, wrong link. Here’s the correct link.
Thanks for this series. In the previous post, you said that composition “has plenty of goodness to it,” but you also call composition a “distraction.” When would you recommend using composition in a JavaScript project?
What I meant is that composition is one of many distractions that lead you into believing that behavior delegation is actually some bizarre sort of inheritance. When used on its own, composition is fine, but it shouldn’t be used in the context of creating “classes” as a workaround for the weirdnesses of JS’s system.
As an example, you could have a
Router
object that you expose as a.router
public property on yourUIManager
object, as opposed to wiringUIManager
to delegate toRouter
via[[Prototype]]
. This would be composition, as a property on an object that is itself some other object.What would sorta introduce confusion/distraction is if you used composition to hide the fact delegation wasn’t working the way you wanted classes to work.
You can of course mix both patterns in the same project, but it’s HOW you use composition that matters, because it can be used for good or used as a tool of confusion by the inheritance crowd.
The goal is to keep these concepts separate (and use them independently to their full power) instead of using one to confuse/conflate the other. Behavior delegation has been so confused and overlooked because of all the other OO jazz that I really wanted to set all that aside so we could examine BD all by itself.
Does that clarify what I mean?
Yes, that makes sense. Thanks again!
Great Post. I’m with you Constructor Functions only make things confusing.
I always heard that Constructor Functions are a lot more performant than Object.create, so I made a test: http://jsperf.com/object-create-vs-constructor-function.
At least in Chrome (V8) that seems to be the case. So this is a little gotcha of going the Object.prototype way.
I believe this has to do with the javascript engine. If you use a constructor function and don’t add or remove any of the instance properties after instantiation then the engine can cache the type of an object which speeds things up.
Yes, this is a difference between javascript engines. I’ve made a revision of your jsperf test using the two full code samples above at http://jsperf.com/object-create-vs-constructor-function/3. You can see that new is about 38% faster in Webkit, while Object.create is about 43% faster in Firefox. In Opera new is about a 4% faster. So you can see how the differences in the optimizations made by different js engines have a significant impact on the performance of the two styles.
I meant Object.create way.
Nice article. I think this is a really interesting approach to defining “class” structures in js. However, I’m going to challenge your claim that this method works ‘without any of the confusion/distraction of “constructor functions”, new, .prototype, etc.’ All of those aspects are still there, just hidden within Object.create(). The polyfill implementation from mozilla’s site gives a glimpse into what’s going on inside Object.create():
The technical aspect of creating constructor functions, setting up the prototype, and calling ‘new’ is still happening. This is evidenced by the fact that b1.constructor === Object. So at minimum the Object() function is getting called.
The diagrams in this article depict purely conceptual differences. There’s no technical difference between using “new” or Object.create(). All the same constructor functions and prototype structures are getting created. It’s just happening behind the scenes with Object.create().
That said, the coding approach you’ve outlined here is easy to understand and certainly clarifies delegation vs. inheritance concept. The only downside I see is that it breaks the use of ‘instanceof’, but I think that is acceptable given the option of using isPrototypeOf().
On a side note, I’d recommend using a convention where the ‘init’ method returns ‘this’ to allow object creation to be chained:
Thanks for the article. It certainly got me thinking about things in a new way.
unfortunately isPrototypeOf only checks the prototype back one level, you may want to know if it’s got a certain prototype further back in the chain, though we can always fix that with something like:
@rhys – I think you’re mistaken.
isPrototypeOf()
will check all the way up the chain, given the right context. So, in my coding example,Foo.isPrototypeOf(b1)
is true, even though there’s two levelsb1 -> Bar -> Foo
.@db – I think there’s a number of differences that make
Object.create()
different from constructor-style coding:1. the fact that all objects are created (or at least appear to be) by the
Object()
constructor doesn’t mean that constructors are happening throughout the rest of the code base. That’s basically a hidden implementation detail. AIUI, the JS engine is free to construct those objects however it wants, as long as the behavior stays the same, which is that theObject.prototype
has a constructor that points toObject()
. In short, I’m not entirely certain that those objects ARE actually created byObject()
, but even if they are, that where the constructors stop in my example, as opposed to being strewn throughout.2. Again, implementation-wise, I don’t think the
Object.create()
actually needs to create an empty constructor function just to throw it away after creating our new object and linking it. Yes, that’s how the polyfill looks, but I don’t think necessarily that’s how it happens under the covers. Certainly, ONE BIG difference is that IF the constructor exists at all, it is hidden away and does not end up polluting the namespace.3. Another difference with
Object.create()
is that you can actually create an object that has absolutely no[[Prototype]]
, by callingObject.create(null)
, which is NOT something you can do with normal constructor-style coding.I stand by my claim: “without any of the confusion/distraction of ‘constructor functions’, new, .prototype, etc.” because none of those things are EVIDENT in the simplified code, which means that our code style is cleaner and easier to reason about, which was the whole point of the series. This isn’t a blog post about implementation details inside the JS engine, it’s about how we clean up our code style to reveal the true power of JS’s
[[Prototype]]
instead of shrouding it in the other confusions.Great posts … ANd thanks for all comments’ conttributors too
Series was great for a newbie to OOP aspect of JS like me… I learnt a lot on OOP aspect of JS
What about static members?
Nice articles!
Excellent post and series Kyle. I enjoyed comparing the outlined solution to what I do today. FWIW, I tend to go back and forth between “.constructor” and Object-based (i.e. Object.create).
Definitely a trade-off and I still don’t see one being better than the other in “All” situations, unless the only goal is a simpler mental model. Object.create wins easily here.
That being said, there is one thing I’ve been unable to work out with `Object.create` and `Object.getPrototypeOf` (to be fair, I haven’t been sufficiently motivated to solve this since I’ll happy lean on constructors to get the job done). Unless I am missing something simple, don’t we have a minor x-browser issue?
The `Foo` object has no `.constructor` property and `__proto__` is not available everywhere. This makes it difficult to properly polyfill `Object.getPrototypeOf `.
Have you been able to find a clean workaround?
Great question, Wil. I actually have looked into that, and was going to highlight it here but the article series was already pretty long and in-depth.
I’ve played around with this notion of an
Object.make(..)
helper that does whatObject.create(..)
does, but also makes sure that every object it “makes” has, at minimum, a__proto__
and (delegated) access toisPrototypeOf()
.”>https://gist.github.com/getify/5226305″ rel=”nofollow”>anObject.make(..)
helper that does whatObject.create(..)
does, but also makes sure that every object it “makes” has, at minimum, a__proto__
and (delegated) access toisPrototypeOf()
.Object.make(..)
helper that does whatObject.create(..)
does, but also makes sure that every object it “makes” has, at minimum, a__proto__
and (delegated) access toisPrototypeOf()
.”>This means that not only can old browsers still benefit from this approach, and use a simple polyfill for
Object.getPrototypeOf()
(which relies on__proto__
), but even in new browsers, if you doObject.create(null)
, you end up getting a truly empty object that is NOT linked toObject.prototype
, so it won’t have__proto__
orisPrototypeOf()
either, which is a bummer.Object.make()
normalizes that by adding back those, as bare minimum additions. Hope maybe that helps.Doh. Sorry for the busted link. Here’s the correct link to Object.make().”>https://gist.github.com/getify/5226305″ rel=”nofollow”>correct link to Object.make().
Argh. https://gist.github.com/getify/5226305
I thoroughly enjoyed your 3-part article. What made it so much easier to understand was the visual representation that you gave, in addition to the well-explained subject with minimal code. I hope your upcoming books will be in a similar style.
The only thing I have a hard time wrapping my head around is how to best handle this n that. Should I still use the that = this; pattern to access it within a function or is there a cleaner way I’m overlooking?
Jorge: there are 4 ways that
this
gets set inside a function execution.Was the function called with
new
? If so, the use the objectnew
created.Was the function called with
call
orapply
specifying an explicit this? If so, use it!Was the function called via a containing/owning object (context)? If so, use it!
DEFAULT: global object
When you learn those and all the different ways they play out (like using hard-binding with
Function#bind()
, etc) then you much more rarely need to mix `this` with closures, which is what thevar self = this;
kind of coding is all about. Every once in awhile you do it, but once you learn closures and `this` separately and deeply, the awkward mixes of them are reduced.BTW, one of the books in my You Don’t Know JS book series kickstarter is going to be all about this exact topic!
Awesome, thanks for the response. I thought you’d be nice enough to leave an answer.
I’ve already chipped in for the books, just wanted to know about “this” straight-away so I can continue coding and researching on my own terms.
Take your time with the books, make them good.
I can’t wait :)
PS: thanks for your answer too Wil
Hi Jorge,
Regarding `that = this`…You will likely get a different answer from just about anyone you ask; however, what I tend to come up with a name that makes sense given the context.
Instead of:
If
this
is an audio player, I’ll do:This is a great writeup. However, I’m running into some of the problems the other commenters have inquired about. I’m trying to figure out how to make this version of JS coding work for my purposes (as well as many other people’s, I’m sure), but it seems like there are things missing that classical OOP style handles more gracefully. This seems like a great idea in theory, but stumbles in a lot places for practical use. It would be awesome if you wrote a follow-up to all this that showed some practical implementations using this. There’s a lot of frameworks out there shimming class-based practices onto JS like the MV* and Widget libraries and many of them heavily utilize polymorphism to initialize up the chain and extend/override parent properties.
I guess I’m putting out the challenge to put your money where your mouth is on this one. I’m definitely not one of the JS masters so my understanding is probably more limited than yours, but I’ve got a solid understanding of writing JS and I was trying to figure out how to build the foundations of a UI/Widget framework for my company using this method you excellently outlined, but I’m running into limitations (also taking into consideration my aforementioned skill level with JS).
Thanks again for the great write-up!
I’d love to know more specifics on issues you’re having. But I agree we need to see real code, not just academic ideas. When/if I get something more concrete to show you, I will post back here. Also, watch my blog. :)
How about Strictly-Objects as opposed to Object-Based? The latter has the same “fuzzy” feel to it as Object-Oriented does; neither really explains what it means.
I now use “OLOO” (objects-linked-to-other-object) to contrast with “OO”.
Finally I can understand what all this .prototype stuff is all about! It makes so much more sense if you stick to patters for which the language was originally designed for.
This was a good writeup. Do you have any real life projects that are using the object only model? Or do you know of any popular libraries that are doing this?
I finally understood ! I read dozens of articles and none was clearly saying things. Thank you so much!
Hi.
Looking at the Inspector in browser’s Web Developer Tools, things look virtually the same.
With no subclass (no Bar), the init function for prototype replaces the constructor function for classical.
With subclass (with Bar), we have an additional constructor function for every subclass, for “classical”.
I know I can trust the Inspector. Your diagrams…?
That’s not correct. In the above final objects-only code snippets,
init()
exists **only** onFoo
object, and will not be duplicated on Bar or on any of theb1
,b2
,… or evenb997
. Delegation will “share” that method across all those objects, which means there’s only oneinit()
that they all use.Moreover, the objects-only style of code produces the exact same objects and the exact same [[Prototype]] relationships as function-constructor style code, but with vastly less code complexity.
Best javascript article ever!! I was looking exactly for this :) I’ve been reading dozens of blogs and books about how to write “good” object-oriented javascript code. But I was shocked by the creepy solutions proposed to do that. Thank you very much for enlighten us!!
I’m still trying to digest this. It seems it will lead to more succinct code for myself, and understandable. I’ve been inspecting objects and properties, and comparing to my current style’s results. It makes sense, but I’m lost on a few points. I really need to have multi-level initialization. In much of our work, we do:
Initially
newprop
is the only property ofsb2
not inherited through the prototype chain. Updatingprop1
viaBase2.init()
keeps the property as an inherited one; however, updating or setting any property from the instance will make the property a direct one of sb2 instead of an inherited one. For example,sb2.prop1 = 'newvalue'
orsb2.prop2 = 'prop2val-set-by-instance'
So, considering all. What is wrong with just calling
Base2.init()
from a redefinedsb2.init()
?I think that just the usage of naming class style naming conventions in my example leads to confusion. As noted (many times) these aren’t classes, just objects. So, in the above, ‘prop2’ isn’t even defined until Base2’s init is called. Anyhow, I’m still questioning what problems would arise from calling Base2.init() from sb2.init(). Also, since updating the value of any inherited property from the instance is essentially eliminating the inherited property and creating a direct one, if we wanted to maintain the inherited properties we’d need to access all via setter functions.
Oh, here’s my inspect():
Nevermind. I think I understand now. When overriding/resetting sb2.init() and then calling Base2.init() from within it, I’m simply setting Base2’s properties after I’ve gotten properties from it. This makes it so that sb2 no longer has it’s own ‘prop1’, and ‘prop1’ is only accessible from the sb2 instance due to the prototype chain (?)
What are the performance implications of this pattern? From what I understand, an important reason for use of .prototype is to attach methods that are only created once, on an object’s constructor’s prototype, rather than being recreated with every call of the new keyword. Is this true of your object->object method?
In other words when i do
am I calling a new object method (therefore adding to memory usage) or calling up the delegation chain and using Bar.init (afaik in the ‘classical’ method i would call a pre-existing method on the prototype, not creating a brand new method).
With the objects-only style of code, you are still creating exactly the same objects with exactly the same [[Prototype]] linkages as you do with function-constructor style. So, the performance characteristics will basically be identical, because everything’s still the same.
The difference with objects-only style code is that it’s vastly simpler to write and reason about. But the end result is functionally pretty much identical.
I am very happy to read this. This is the type of info that needs to be given and not the accidental misinformation that’s at the other blogs. Appreciate your sharing this greatest doc.
Hi,
nice article. But… correct me if I’m wrong – this concept fails on object properties that are objects themselves.
Yes, but that would fail identically in the function-constructor style of code too. What you’re observing (correctly) is that storing data on a delegated-to object is BY-DESIGN going to be shared data with everyone else. This is exactly the same truth as if you had put a property on the “parent class (object)” that you instantiated/inherited from in function-constructor style.
Why is the data shared? I totally don’t get it. :D
how about that ugly way?
displays:
-> http://jsfiddle.net/t9SZt/
This is a watershed article for me on how to effectively do JavaScript “OB” programming – thanks, and good work! I will be doing a lot of re-factoring!
Why not return
this
frominit( ... )
so you can do the following?That’s a perfectly fine suggestion and I do that sometimes. I just didn’t want to muddy the article more with introducing even more different paradigms. :)
I do something like:-
I use to do the same with
create
instead:Here is a possible way to use your very good ideas:
Here is a possible way to use your very good ideas:
arrgghhh! I cannot get the code to display right!
Here is a follow on post of mine inspired by this one:
http://aeoril.github.io/javascript/2014/03/28/object-based-vs-constructor-based-coding-in-javascript.html
Another bad thing for this kind of JavaScript coding style is standard minify/uglify – it does not minifies properties on object, thus script footprint will be larger.
Interesting approach but keeping in mind inheritance and footprint I don’t see any reason to use this kind of approach on anything rather than small “singleton like” object. I think “the prototype base” coding is much better approach.
This is great and very clear, my concerns are memory and performance though. I’m pretty sure that
new
is way faster thanObject.create
but leaves a mess in memory for object relations. Do you have any thoughts about performance and memory Kyle?@Jeremias —
Yes, in fact I do have detailed thoughts on it, and have put that all into a separate blog post here: http://blog.getify.com/sanity-check-object-creation-performance/
TLDR: the speed/memory performance you’re concerned about is almost certainly a non-issue.
Hey Kyle — I find your series awesome, but I do have an issue with the verbose’ness of many of your explanations in relation to new vs. Object.create vs. OLOO etc…
Is it possible you can just list our the benefits, detriments in layman terms of using classical class implmentation, delegation, OLOO etc.. I find myself having to read thru 10pages of your book(s) to find a point but it gets lost in so much sugar/extraneous explanations that one can lose the point before too long … I’d much prefer you list the positives, negatives and then expand on them rather than having the reader try to identify the points you are making and where. Just my 2 cents. For example, you mention how mimicking class creating creates issues.. exactly what are these issues? When in a chain of object creations does this issue come out?
Hi @Kyle!
What do you think about it: https://gist.github.com/danilodeveloper/c62303ff50bd6059dbfd
IMHO it’s more readable because uses Foo.create or Bar.create instead of Object.create(Foo), and ensures that the init function will called.
Thank you so much Kyle, this is exactly what I was after.
Question – It’s been 2 years since you posted this pattern, do you still use OLOO or have you found an alternative?
Yes, I still OLOO. I still use it in the ratio as before, compared to the module pattern, which is about 95% modules, 5% classless-objects-with-delegation (aka OLOO).
An absolute excellent post. Excellent study and presentation. Excellent writing.
Whatever approach a JS coder takes to satisfy a requirement, there is nothing better than understanding the environment in which a choice is made.
Not only have you given your reader the ability to make an informed choice on their approach but you also gave them the ability to create. Nothing incarcerates the mind more than using a lib as a flashlight to find a door to execute a solution.
Only the bold and the beautiful take the time to liberate.
Great article and series!
Really enjoyed reading this, taught me a lot.
I have a question,
How would you override a method and then call the parent’s method?
For example, for init in
b1
?something like this (but one that’s working :))?
How would you do it in an elegant, cross browser way?
Omer,
This is where your thinking has to be re-engineered. Kyle has written a great series of books. You might want to start here (https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch6.md), which builds on what he puts forth in this article.
Hi Rick,
Thank you for the link, I will look through that.
I know, it’s definitely a “re-engineering” as you stated and I like it a lot.
Do you have a TL;DR for me about calling the base method from the child method after overriding it?
Thanks again.
Bar
is still the base object forb1
andb2
.Even in this architecture there *might* be a need to call the base method.
Since we don’t want it to be tightly coupled, I wonder how can we call the base method without knowing the name of that base.
The TL;DR is showing in your replies. There is no ‘base’, there is only OLOO (Objects Linked to Other Objects). And when done correctly, there is no need to ‘call’ a base class, since any request will merely flow down stream (or ‘up the prototype chain’) and be found.
I think that I was mis understood.
I am talking about cases where you want to implement an own object’s method *and after that* call the base method.
Look at the example that I wrote.
Implement b1’s init, do something in there AND THEN call the original b1 init.
Hi,
How can you resolve the storing state issue?
For example, when you are adding an array to Foo and you want it to be unique for every object?
Here’s a jsbin example:
http://jsbin.com/jafaru/1/edit
OK, I get it.
You need to point the array to another memory reference by creating a new array.
Is there a more elegant way to resolve it as I did?
http://jsbin.com/coqipi/1/edit
Hi Kyle,
First of all thanks for this enlightening series of articles. Like many other, I’m still trying to digest it.
I am a CS teacher with a strong C++/Java background. I’m currently writing a introductory course to programming with JavaScript. The OOP chapter is of course the most challenging. I have written an example program a bit more complicated than yours to illustrate the main OO concepts, using the new ES2015 syntax (see gist). As you may have guessed, i find it clean and expressive.
However, I have chosen to stick with ES5 for now and must translate this example to the “old” syntax. I came across your work and tried to use the OLOO style, but I still cannot mimic the sheer elegance and expressiveness of class-based OO. OK, I may be biaised :)
Maybe this example will be of interest to other would-be JS coders out there.
Thanks in advance for your advice.
https://gist.github.com/bpesquet/6dac5adeb5f31fdc1951#file-rpg_es2015-js
After a bit of research, I came along with another solution (hopefully) written in the OLOO style :
https://gist.github.com/bpesquet/c7b9ac3745b38ccc2827#file-rpg_oloo-js
Anyway, these articles just made be a little bit better at JavaScript, so thanks again !
Hey Baptiste,
Glad to see that you wrote up a couple POCs. That makes the discussion a bit more informed. I took your example a little further using just Object.create, Object.assign, and functions (no constructors, no new, no class):
https://github.com/wilmoore/polykata/tree/master/oloo-game-player-poc/js#oloo-game-player-poc
Thx very much for your great exposure. It turned me over to OO.2
I wonder if the following two declarations are equivalent from the Object Only perspective:
…
I prefer declaration 2 for its visual appearance in case of complex situations, but wonder if it implicitly invokes the constructor.
Hello. Coding (for real) in JS since about one year, I’ve started to read articles about Eric Elliot thoughts, etc, and ended up there. Beyond this great article and the time you spent writing it, I have a question.
When you do
a major difference by doing
is that in the first case, the speak method is duplicated each time you will instantiate a new object with Object.create, while it won’t in the second case.
Am I right ?
Nope. :)
In this case, both
a
andb
have the same shape, which is that neither of them got a copy of thehello()
method. Both of them delegate up the[[Prototype]]
chain. It’s just thata
delegates to a weirdly namedFoo.prototype
object, whileb
delegates to a more sensibly namedBar
object.Otherwise, same shape, same usage characteristics and performance, etc.
I’m almost mindfucked seeing your second diagram – that the prototype of the Function() constructor having call, apply & bind methods is a function represented by a circle, rather than a square which represents an object like the rest of prototype objects in the diagram.
Please tell me that this is a mistake, or was done intentionally to trick people into thinking that its literally impossible to mentally digest the constructor based way of coding.
@Tilwin-
Nope, no mistake, and not a conspiracy either. The reason that
Empty
is a function rather than just another plain object is that all functions gain their ability to be functions (and not just objects) from that first function.Regular functions in your program delegate to
Empty
to get access toapply(..)
, etc, but they also “delegate” to (actually inherit from)Empty
for their ability to be executable.@Kyle Thanks, What you’re saying makes more sense now. (I did some further reading after adding the comment. Even though I haven’t got my head around those stuff completely, this diagram is very helpful :)
Hello sir,
Wonderful post.I saw the style of inheriting behaviors with delegation.You wrote it without the use of constructor function and “new” keyword.But you used Object.create which itself internally used “new” keyword.
If Object.create is using “new”.Is it that bad to use “new” keyword compared to what is explained in the article.
Hi @Kyle,
Thanks a lot for you explaination, i tried this code-style and it works like a charm.
But, i noticed somethings strange during my tests. Look at this code snippet :
It seems that TotoBrother.charac.foo points to its delegated Object (Toto.charac.foo), affecting its value !!! But if you overload with an object, things goes right after…
Tested on Chrome 49.0 and FireFox 44.0.2
Thanks a lot again !
For comprehension, a make mistake in my comments, should be :
Thanks a lot
Great article, but what about encapsulation? Your approach does not allow the use of private variables.
This is a good article, very well written – but
Object.create
feels sloppy to me in the examples provided – maybe its because I’m used to far more strictly typed “Class” languages?Why define
Foo
then doFoo.Prototype.Whatever=function
afterwards? Why not just definethis.Whatever=function
withinFoo
and haveBar
reference that? You’re definingnew Foo()
anyway so what benefit does Prototyping have in that scenario? Foo and Bar are now very clearly linked without the use of prototyping.Using prototype as a method to inherit another object (‘Model’ Inherits ‘BaseModel’ for example) purely to read that object’s properties is nice and quick – but if I try to write to
foo.bar
the prototypefoo.prototype.bar
disappears and and I end up with the propertybar
in objectfoo
– headaches ensue.So it feels sloppy to me. I’m sure it has its benefits but for the type of coding I do I think I’ll just stick with the
new
keyword.Could you or someone else provide more practical examples? IE: In C# I’d have class Person inheriting class AddressDetails and be able to read and write Address information.
@Ben
> Why not just define this.Whatever=function within Foo and have Bar reference that?
IIUC, this represents a fundamental misunderstanding of the mechanism. Consider these two, which it sounds like you’re claiming are functionally identical:
function Foo() {} Foo.prototype.hello = function() { console.log(“hello”); };
var f = new Foo();
This series is great and very useful, and I certainly have to chew it a lot more to grasp all that data. Yet, like “D” asked above and was not answered: My main issue with this is encapsulation. It’s possible only method-by-method, and there’s nothing the object as a whole can use as private data. I avoid using “new” for sometime already, but the main method I use so I can encapsulate is sort of a factory that returns an Object with methods, while the private parts (methods and members) are within the factory function.
How can it be simulated with the object-linked methodology?
there’s something strange in the first diagram,
speak()
‘s.__proto__
is pointing toidentify()
which implies that identify.prototype is the reference link ofspeak()
but of course that’s not the case, beside the log shows that the only relation betweenspeak()
andidentify()
is that one (speak()
) is a direct property ofBar.prototype
and the other is a method of its__proto__
link, so whyspeak()
is pointing to identify? what did I miss?You’re misreading the diagram. It’s not saying that `speak()`’s __proto__ is pointing anywhere.
`speak()` is property on an object (the square around it), which is commonly known as `Bar.prototype`, and thus it is `Bar.prototype.__proto__` that is pointing to another object (square) called `Foo.prototype`, which itself happens to hold another property: `speak()`.
I know this article is quite old now but it looks like the instanceof in your last example does not work.
Calling instanceof with an object returns this error:
Has the behaviour of instanceof changed since this article was originally written?