I used to think that the important innovation of JavaScript was prototypal inheritance. I now think it is class free object oriented programming. I think that is JavaScript’s gift to humanity. That is the thing that makes it really interesting, special, and an important language.
Douglas Crockford: The Better Parts – JSConfUY 2014
Tell Me More
I think Douglas Crockford is not only on to something here, but completely right. Let’s talk about what it means, why it is hard to grasp for some programmers, and how it can benefit you.
What Class Free Object Oriented Programming Means
First, we need to cover what Object Oriented Programming (OOP) means. There are many types of OOP that are in use today.
Class Based OOP
This is your classical example of OOP. The easiest to understand form is single inheritance. You make a class and inherit from it. And subsequent classes can inherit from that class. One root class, and a tree of classes stemming from there.
This paradigm is super easy to grasp, but also super easy to create traps for yourself. Like JavaScript, this methodology is full of foot-guns.
A podiatric penetration purposed pistol. A gun which is apparently designed for shooting yourself in the foot.
A Duck, an Alligator, and a Goat
Let’s say we want to create a Duck
class in this paradigm.
- Inherit from the base
Object
and create anAnimal
class. - Inherit from the
Animal
class and create aDuck
class. - Define
swim
,walk
,talk
, andfly
methods onDuck
.
Easy right? But wait a second. New requirements have come in and you need to define Alligator
and Goat
now.
- Inherit from
Animal
and create anAlligator
class. - Define
walk
,swim
, andtalk
onAlligator
. - Inherit from
Animal
and create aGoat
class. - Define
walk
andtalk
onGoat
.
All done! Okay, time to refactor and clean up. walk
, swim
, fly
, and talk
are not specific to those animals are they? We should probably move them to a shared class to be more DRY. We should move the methods to Animal
so they can be shared among the animals and written once. Cool, now walk
, talk
, fly
, and swim
are written only once!
But wait, Alligator
shouldn’t be able to fly
! We need an additional class between Animal
and the animals to account for this. Let’s create a FlyingAnimal
inheriting from Animal
so we can account for the Duck
that flies. Oh, and Goat
doesn’t swim
like Duck
and Alligator
. So we need to create a SwimmingAnimal
to account for that and assign Alligator
and Duck
to it, but not Goat
. But wait, Duck
can’t inherit from FlyingAnimal
in addition to SwimmingAnimal
and WalkingAnimal
!
This is why a single inheritance taxonomy is not a good way of organizing our objects in programming. Most of the languages have ways of accounting for this. But to me, they at best feel like workarounds. It almost certainly possible in all the most common languages, but they really get in the way and require you to write tons of boilerplate to do it.
Class Free OOP
This is where languages like JavaScript really help out. JavaScript Functions
are first-class citizens in the language, so this method just feels natural and is relatively frictionless.
If you want a Duck
, you create a Function
that returns an Object
that looks like a Duck
. If you also want an Alligator
and a Goat
, you create those two Functions
. The difference is you do not inherit any classes or implement any interfaces. Inside the functions, you add methods and properties and return an Object
that looks like what you want.
But this time around, when you refactor out the reusable portions, you just take the pieces out into their own Objects
: withFlying
, withAnimal
, withWalking
, and withSwimming
. Then for each Object
that needs the functionalty, you just copy the methods on them to your own Object
. And if you want to reuse any of them in another object, you just do. No compiler is going to complain about it or tell you that you can’t. It is just going to work and act like a duck.
Why Class Free OO is Hard To Grasp
It is a Whole School of Thought
Most students in Computer Science or people that have self-taught themselves programming have learned or picked up a popular OO language. Whether that is Java, C#, C++, etc. does not matter. These languages have inheritance built into them and strongly rely on you using their pattern to properly use the language. JavaScript even has its own paradigm of it: Object.prototype.
For many, the current paradigm for handling complexity is by utilizing interfaces and parent classes. This works, but can get hairy when trying to call methods of parents or parent’s parents to extend functionality or trying to decide what should be in a class, parent, or interface.
Tools are Geared Around Inheritance
People are too dependent on their OOP focused IDE’s to even want to do it another way. Many of the restrictions and constraints of some languages make programming so difficult an IDE is more-or-less required. If I had to program C# code without an IDE, I would probably go nuts. The sheer amount of ceremony, boilerplate, keywords, system libraries, constraints, and gotchas in the language are too much for me to try and program with a text-editor and a compiler. The IDE’s offer all these tools for refactoring in an OOP sense it is hard to stray away. Some people spin this as a benefit for the language because it has such an amazing IDE, but I see it as a negative that the IDE is basically required to use the language in a proficient way. The language is so complicated, not even the programmers can trust themselves.
How Class Free OOP Can Benefit You
It actually can’t benefit you if your language of choice can’t do it in an easy way. But if it is like JavaScript, there are lots of niceties you get out of it. The weirdest thing for me is how simple of a language JavaScript is, yet how flexible it is and how confusing it is to even experienced developers.
But in JavaScript at least, here are some things it gives you:
- Smaller amounts of code
- Simpler code that saves time
- No crazy inheritance/interface graphs
- Easily composable objects
Let’s go over those real quickly.
You will write less. You aren’t writing boilerplate to define interfaces and figuring out the hierarchy and implementing the functions on the class. You are copying the properties straight to your object. You can sort of view it as multi-inheritance limited to a single level where all of the parents are merely describers.
Your code will be much simpler and flexible. After you’re done not writing tons of boilerplate and ceremony, pat yourself on the back for saving that time and being able to see your bugs more easily.
You won’t be making any decisions about if your parent’s parent’s parent class is doing too much or too little. You’ll be looking at the immediate thing you included and deciding if it is enough.
Do you have a button that needs rounded corners? Cool, make a rounded-corners object. Do you need a duck that has rounded corners too? Cool, then include your rounded-corners object without worrying about what they were inherited from. Nobody cares about what you do with your objects in your private time.
A Duck, an Alligator, and a Goat Revisited
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function newAlligator () { | |
var alligator = { | |
name: 'alligator', | |
word: 'grrr' | |
}; | |
extend(alligator, withAnimal); | |
extend(alligator, withWalking); | |
extend(alligator, withSwimming); | |
return alligator; | |
} | |
function newDuck () { | |
var duck = { | |
name: 'duck', | |
word: 'quack' | |
}; | |
extend(duck, withAnimal); | |
extend(duck, withWalking); | |
extend(duck, withFlying); | |
extend(duck, withSwimming); | |
return duck; | |
} | |
function newGoat () { | |
var goat = { | |
name: 'goat', | |
word: 'baa' | |
}; | |
extend(goat, withAnimal); | |
extend(goat, withWalking); | |
return goat; | |
} | |
var alligator = newAlligator(); | |
alligator.talk(); | |
alligator.swim(); | |
alligator.walk(); | |
var duck = newDuck(); | |
duck.talk(); | |
duck.fly(); | |
duck.walk(); | |
var goat = newGoat(); | |
goat.talk(); | |
goat.walk(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function extend (target, source) { | |
Object.keys(source).forEach(function (key) { | |
if (typeof target[key] !== 'undefined') { | |
return; | |
} | |
target[key] = source[key]; | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var withAnimal = { | |
name: 'name', | |
word: 'word', | |
talk: function () { | |
console.log(this.name + ' says ' + this.word); | |
} | |
}; | |
var withFlying = { | |
fly: function () { | |
console.log('flap flap'); | |
} | |
}; | |
var withSwimming = { | |
swim: function () { | |
console.log('splish splash'); | |
} | |
}; | |
var withWalking = { | |
walk: function () { | |
console.log('stomp stomp'); | |
} | |
}; |
[…] Classlessとの関係で見るならこちらの記事(Class Free Object Oriented Programming)がわかりやすいです。 […]
LikeLike
[…] Classlessとの関係で見るならこちらの記事(Class Free Object Oriented Programming)がわかりやすいです。 […]
LikeLike
I like this with the new bind operator that is coming out:
function talk() {
console.log(this.name + ‘ says ‘ + this.word);
}
function newDuck() {
return {name: ‘duck’, word: ‘quack’};
}
const duck = newDuck();
duck::talk();
LikeLike
I was excited about
::
for a whole different reason. But this is a really cool usage pattern for::
I hadn’t thought of before! Thanks for sharing!LikeLike
I didn’t know about the :: operator. That’s exciting.
LikeLike
What would the duck alligator example look like if written with prototypal inheritance? Up until quite recently people were pretty vocally favoring prototypal over classic inheritance. Is object free now the shiny new paradigm to follow? If so what benefits does it have over prototypal?
LikeLiked by 1 person
This is it with prototypical inheritance: http://jsbin.com/jocecaxahi/edit?js,console
I prefer the class free approach because it tends to think more in sets of behaviors. Whereas prototypical tends to think of individual methods and properties.
Another thing to notice is that Animal really doesn’t offer much as a class because alligator, duck, and goat share so much, but no 3 have everything.
I welcome people to look at my prototype example and offer any tips to make it less heavy in syntax.
LikeLiked by 1 person
Also, what you are doing is behavior composition, an idea from the 70s that was lost in the craze of OOP. And you are absolutely correct that composition > inheritance. Although a small, shallow inheritance chain doesn’t make me feel unclean.
Behavioral composition just feels like a better model for my slightly aspergers brain. You have found yourself a new fan!
LikeLike
Great post, Danny. I’ve been trying to wrap my head around class free OOP for some time now. Your article brought it home for me. Thx.
Btw here’s a less syntax heavy version in es6.
function newAnimal(spec = {}){
let { name = ‘it’, word = ‘nothing’ } = spec,
talk = () => console.log( name + ‘ says ‘ + word);
return Object.freeze({ name, word, talk });
}
function newAnimalMovement(spec = {}) {
let { name = ‘it’ } = spec,
fly = () => console.log(name + ‘ is flying: flap flap’),
swim = () => console.log(name + ‘ is swimming: splish splash’),
walk = () => console.log(name + ‘ is walking: stomp stomp’);
return Object.freeze({ fly, swim, walk });
}
function newAlligator(spec){
let { name, word, talk } = newAnimal(spec),
{ walk, swim } = newAnimalMovement(spec);
return Object.freeze({ name, word, talk, walk, swim });
}
function newDuck(spec = {}){
// overide defaults
let { name = ‘duck’, word = ‘quack’ } = spec ,
{ talk } = newAnimal({ name, word }),
{ fly, walk, swim } = newAnimalMovement({ name });
return Object.freeze({ name, word, talk, fly, walk, swim });
}
See the jsbin here: http://jsbin.com/lepiwupumo/edit?js,console
LikeLike
Wow! Great content in both the article and comments section. The short on the principle of least power was a good juxtaposition.
http://blog.codinghorror.com/the-principle-of-least-power/
LikeLiked by 1 person
Isn’t the point of sharing prototypes to save memory?
This approach is certainly more convenient and flexible, but what are the performance implications?
LikeLike
You are absolutely correct that you lose the flyweight pattern when doing this. Memory is not normally a concern for most applications. If you know memory is a huge pain point for your application it would probably be good to stick with prototypes or some other solution.
https://en.wikipedia.org/wiki/Flyweight_pattern
LikeLike
Are you sure that you will lose flyweight? It seems you are only copying those functions by reference to another object, so you don’t need to re-create them on every instance construction.
LikeLiked by 1 person
I appreciate your illustrative examples. Note, though, that your use of “this” violates the rule that led Crockford to this style. I have reworked your examples in my own blog entry on the subject: http://whatsroyupto.blogspot.com/2015/05/class-free-object-oriented-programming.html
Unlikely to be the last word on the subject, of course.
LikeLike
Excellent! Thanks for the reply and the followup blog post. I am also now of the opinion that “this” is something to be avoided. I think your blog post is awesome. 🙂
LikeLike
perl has had ‘class-free’ elements available for several years. it’s amusing when new languages talk about features and give them funny names and pretend they have something new when I’ve been using this stuff for years.
LikeLike
Almost everything new these days in software development is a rebranding of something old. Especially in the JavaScript world where everything seems new again to developers.
http://blog.codinghorror.com/the-principle-of-least-power/
Thanks for the comment. 🙂
LikeLike
This seems to be the idea behind traits and “composition over inheritance” (http://en.wikipedia.org/wiki/Composition_over_inheritance). Here it is a Scala version (where traits are a basic language feature):
The boilerplate in JavaScript is daunting. Maybe JavaScript should introduce a construct for that rather than classes.
LikeLiked by 2 people
Thanks for reading!
Languages like Rust and Scala have trait systems that are great! I’ve looked at Rust quite a bit since writing this post a few weeks ago. I wish I could run languages on the web as easily as I can run JavaScript on the web sometimes. 🙂
http://rustbyexample.com/trait.html
Also, classes in JavaScript was a terrible thing for ES6 to add. It is only going to further confuse people.
LikeLike
I am a bit ambivalent about the introduction of classes in JavaScript. I think there is a welcome tendency to be more cautious with inheritance these days, as we have other tools, including functional approaches.
But in spite of what Crockford says, standardizing classes in JavaScript might still be right thing to do given that otherwise, people will keep reimplementing incompatible class systems!
By the way, it’s getting easier and more realistic to run other languages in web browsers (see for example http://www.scala-js.org/).
LikeLike