Everyone’s heard stories about recruiters who confuse JavaScript and Java, right? We might chuckle and remind everyone that Java is to JavaScript as car is to carpet. Which makes it drippingly ironic that today, WE treat Java as the gold standard to measure up to. I’m sure many of you have heard the refrain, in one form or another. “JavaScript’s classes are fake,” they might say, “not like those real classes Java has.” I’d like to persuade you that JavaScript’s classes and inheritance are actually much more traditional and common than we think. I’d like to give you a tour of languages – besides Java – and show how they compare to JavaScript.

First up is Python. Let me admit up front that I once believed the same assumptions that I’m now trying to persuade you are wrong. Years ago I needed to use Python for a project. It was my first time with the language. I got involved with the Python community, sometimes asking questions, sometimes answering. On StackOverflow, someone had asked whether Python could change methods or properties of a class after you define the class. “No,” I answered, “Of course not. Classes are static compile-time things.” I was downvoted. And rightly so. I was wrong. The Python docs didn’t make a big fuss about it, but in there it says:

[classes] are created at runtime, and can be modified further after creation. … When the class object is constructed, the base class is remembered. This is used for resolving attribute references: if a requested attribute is not found in the class, the search proceeds to look in the base class. This rule is applied recursively if the base class itself is derived from some other class.

I scratched my head and thought, “That sounds like prototypal inheritance…” I had to test it out.

Python

class C : def foo ( self ): return True instance = C () # # Before monkey patch, as expected, calling foo works, calling the non-existent method bar does not # instance . foo () # ok instance . bar () # AttributeError: 'C' object has no attribute 'bar' # # After instance already exists, monkey patch the class to add a method # def barFn ( self ): return True C . bar = barFn # # Calling bar works now # instance . foo () # ok instance . bar () # ok # # Monkey patch the class to delete a method # del C . foo instance . foo () # AttributeError: 'C' object has no attribute 'foo' instance . bar () # ok # # After instance already exists, change the class it inherits from / delegates to # class Z : def baz ( self ): return True instance . __class__ = Z instance . foo () # AttributeError: 'Z' object has no attribute 'foo' instance . bar () # AttributeError: 'Z' object has no attribute 'bar' instance . baz () # ok

At the time, I, like many others in the JavaScript community, believed the term “prototypal inheritance,” by definition, was delegation, and that JavaScript was unique in that way. Yet here I was faced with Python, a popular and widespread classical inheritance language – and with all the same abilities and behavior that I thought was uniquely prototypal JavaScript. “Does that mean Python is secretly prototypal?” I thought. I should check more languages. Ruby:

Ruby

class C def foo () return true end end instance = C . new # # Before monkey patch, as expected, calling foo works, calling the non-existent method bar does not # instance . foo () # ok instance . bar () # undefined method `bar' # # After instance already exists, monkey patch the class to add a method # class C def bar () return true end end # # Calling bar works now # instance . foo () # ok instance . bar () # ok # # Monkey patch the class to delete a method # class C remove_method :foo end instance . foo () # undefined method `foo' instance . bar () # ok

Perl was the first programming language I learned, once upon a time. I’ll always have a soft spot for it – but this is gonna get weird. Brace yourself.

Perl

package C ; sub new { my $class = shift ; return bless {}, $class ; } sub foo { my $self = shift ; return true ; } my $instance = C -> new (); # # Before monkey patch, as expected, calling foo works, calling the non-existent method bar does not # $instance -> foo (); # ok $instance -> bar (); # Can't locate object method "bar" via package "C" # # After instance already exists, monkey patch the class to add a method # * C:: bar = sub { my $self = shift ; return true ; }; # # Calling bar works now # $instance -> foo (); # ok $instance -> bar (); # ok

I wanted to check Smalltalk as well, but at the time I couldn’t find something easy like a REPL. Along the way, though, I found a video talk from a speaker who turned out to be ECMAScript Spec Editor Allen Wirfs-Brock. Sadly hardly anyone had seen the video talk, judging by the view count. In it he compares the object models of JavaScript and Smalltalk. “The punchline,” he says in the talk, “is they actually aren’t as different as you might think. … We have the VSE Smalltalk model, and you have what you get with the JavaScript constructor pattern – they look exactly the same, basically.”

Here’s that talk: youtube.com/watch?v=EPHmm8JiGbg

“Does that mean JavaScript is secretly classical?” I thought. I took for granted that I understood what “prototypal” and “classical” meant, and now I felt like I had to re-think it all. What definition of “class” could possibly fit all of Python, Ruby, Perl, Smalltalk, as well we Java, C++, and C#? What definition of “prototypal” could make JavaScript’s behavior distinct from Python? One resource I found useful in this search was the UML Reference Manual. Definitions written by experts in a language-neutral way seemed to be exactly what I was looking for. It defined a class as:

The descriptor for a set of objects that share the same attributes, operations, methods, relationships, and behavior. Semantics: A class is the named description of both the data structure and the behavior of a set of objects. Structure: A class contains a list of attributes and a list of operations that each form a namespace within the class. Inherited attributes and inherited operations also appear within the respective namespaces. Discussion: The concept of class applies to a range of usages in logical modeling, as well as implementation. It includes both the concept of type and the concept of implementation class. [An object] may have multiple classes, as well as be able to change its class at run time.

And inheritance as:

The mechanism by which more specific elements incorporate structure and behavior defined by more general elements. Semantics: Inheritance allows a full description of a generalizable element to be automatically constructed by assembling declaration fragments from a generalization hierarchy. … That is inheritance. It is the incremental definition of an element. Other details, such as method lookup algorithms, vtables, and so on, are merely implementation mechanisms to make it work in a particular language, not part of the essential definition.

Some time later, another (perhaps implied) definition of “class” that stuck with me came in the form of a tweet, and from none other than that same ECMAScript Spec Editor Allen Wirfs-Brock.

JS devs: do you use abstraction? What word do you use for “an open set of objects that share a common interface and implementation”? – https://twitter.com/awbjs/status/689506114807857152

ES6 classes, it turned out, were real. Copy vs delegation, compile-time vs runtime were merely implementation details. And JavaScript’s implementation, surprisingly, wasn’t even an uncommon one.