Understanding JavaScript OOP

Published 09 October 2011

Warning Some parts of this article are made obsolete or not-as-relevant with the publication of ES2015, and these parts need to be revised. All performance commentaries in the article should be ignored.

JavaScript is an object oriented (OO) language, with its roots in the Self programming language, although it's (sadly) designed to look like Java. This makes the language's really powerful and sweet features stay covered by some pretty ugly and counter-intuitive work-arounds.

One such affected feature is the implementation of prototypical inheritance. The concepts are simple yet flexible and powerful. It makes inheritance and behaviourism first-class citizens, just like functions are first-class in functional-ish languages (JavaScript included).

Fortunately, ECMAScript 5 has gotten plenty of things to move the language in the right way, and it's on those sweet features that this article will expand. I'll also cover the drawbacks of JavaScript's design, and do a little comparison with the classical model here and there, where those would highlight the advantages or disadvantages of the language's implementation of prototypical OO.

It's important to note, though, that this article assumes you have knowledge over other basic JavaScript functionality, like functions (including the concepts of closures and first-class functions), primitive values, operators and such.

1. Objects

Everything you can manipulate in JavaScript is an object. This includes Strings, Arrays, Numbers, Functions, and, obviously, the so-called Object — there are primitives, but they're converted to an object when you need to operate upon them. An object in the language is simply a collection of key/value pairs (and some internal magic sometimes).

There are no concepts of classes anywhere, though. That is, an object with properties name: Linda, age: 21 is not an instance of the Object class (or any class). Both Object and Linda are instances of themselves. They define their own behaviour, directly. There are no layers of meta-data (i.e.: classes) to dictate what given object must look like.

You might ask: "how?"; more so if you come from a highly classically Object Orientated language (like Java or C#). "Wouldn't having each object defining their own behaviour, instead of a common class mean that if I have 100 objects, I will have 100 different methods? Also, isn't it dangerous? How would one know if an object is really an Array, for example?"

Well, to answer all those questions, we'll first need to unlearn everything about the classical OO approach and start from the ground up. But, trust me, it's worth it.

The prototypical OO model brings in some new ways of solving old problems, in an more dynamic and expressive way. It also presents new and more powerful models for extensibility and code-reuse, which is what most people are interested about when they talk about Object Orientation. It does not, however, give you contracts. Thus, there are no static guarantees that an object X will always have a given set of properties, but to understand the trade-offs here, we'll need to know what we're talking about first.

1.1. What are objects?

As mentioned previously, objects are simple pairs of unique keys that correspond to a value — we'll call this pair a property. So, suppose you'd want to describe a few aspects of an old friend (say Mikhail), like age, name and gender:

http://dl.dropbox.com/u/4429200/blog/oop-obj-mikhail.png

Objects are created in JavaScript using the Object.create function. It takes a parent and an optional set of property descriptors and makes a brand new instance. We'll not worry much about the parameters now.

An empty object is an object with no parent, and no properties. The syntax to create such object in JavaScript is the following:

1
var mikhail = Object.create(null)

1.2. Creating properties

So, now we have an object, but no properties — we've got to fix that if we want to describe Mikhail.

Properties in JavaScript are dynamic. That means that they can be created or removed at any time. Properties are also unique, in the sense that a property key inside an object correspond to exactly one value.

Creating new properties is done through the Object.defineProperty function, which takes a reference to an object, the name of the property to create and a descriptor that defines the semantics of the property.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object.defineProperty(mikhail, 'name', { value:        'Mikhail'
                                       , writable:     true
                                       , configurable: true
                                       , enumerable:   true })

Object.defineProperty(mikhail, 'age', { value:        19
                                      , writable:     true
                                      , configurable: true
                                      , enumerable:   true })

Object.defineProperty(mikhail, 'gender', { value:        'Male'
                                         , writable:     true
                                         , configurable: true
                                         , enumerable:   true })

Object.defineProperty will create a new property if a property with the given key does not exist in the object, otherwise it'll update the semantics and value of the existing property.

By the way

You can also use the Object.defineProperties when you need to add more than one property to an object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object.defineProperties(mikhail, { name:   { value:        'Mikhail'
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true }

                                 , age:    { value:        19
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true }

                                 , gender: { value:        'Male'
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true }})

Obviously, both calls are overtly verbose — albeit also quite configurable —, thus not really meant for end-user code. It's better to create an abstraction layer on top of them.

1.3. Descriptors

The little objects that carry the semantics of a property are called descriptors (we used them in the previous Object.defineProperty calls). Descriptors can be one of two types - data descriptors or accessor descriptors.

Both types of descriptor contain flags, which define how a property is treated in the language. If a flag is not set, it's assumed to be false — unfortunately this is usually not a good default value for them, which adds to the verbosity of these descriptors.

writable

Whether the concrete value of the property may be changed. Only applies to data descriptors.

configurable

Whether the type of descriptor may be changed, or if the property can be removed.

enumerable

Whether the property is listed in a loop through the properties of the object.

Data descriptors are those that hold concrete values, and therefore have an additional value parameter, describing the concrete data bound to the property:

value

The value of a property.

Accessor descriptors, on the other hand, proxy access to the concrete value through getter and setter functions. When not set, they'll default to undefined.

get ()

A function called with no arguments when the property value is requested.

set (new_value)

A function called with the new value for the property when the user tries to modify the value of the property.

1.4. Ditching the verbosity

Luckily, property descriptors are not the only way of working with properties in JavaScript, they can also be handled in a simple and concise way.

JavaScript also understands references to a property using what we call bracket notation. The general rule is:

1
<bracket-access> ::= <identifier> "[" <expression> "]"

Where identifier is the variable that holds the object containing the properties we want to access, and expression is any valid JavaScript expression that defines the name of the property. There are no constraints in which name a property can have1, everything is fair game.

Thus, we could just as well rewrite our previous example as:

1
2
3
mikhail['name']   = 'Mikhail'
mikhail['age']    = 19
mikhail['gender'] = 'Male'
> Note > All property names are ultimately converted to a String, such that `object[1]`, `object[⁣[1]⁣]`, `object['1']` and `object[variable]` (when the variable resolves to `1`) are all equivalent. {: .note}

There is another way of referring to a property called dot notation, which usually looks less cluttered and is easier to read than the bracket alternative. However, it only works when the property name is a valid JavaScript IdentifierName2, and doesn't allow for arbitrary expressions (so, variables here are a no-go).

The rule for dot notation is:

1
<dot-access> ::= <identifier> "." <identifier-name>

This would give us an even sweeter way of defining properties:

1
2
3
mikhail.name   = 'Mikhail'
mikhail.age    = 19
mikhail.gender = 'Male'

Both of these syntaxes are equivalent to creating a data property, with all semantic flags set to true.

1.5. Accessing properties

Retrieving the values stored in a given property is as easy as creating new ones, and the syntax is mostly similar as well — the only difference being there isn't an assignment.

So, if we want to check on Mikhail's age:

1
2
mikhail['age']
// => 19

Trying to access a property that does not exist in the object simply returns undefined 3:

1
2
mikhail['address']
// => undefined

1.6. Removing properties

To remove entire properties from an object, JavaScript provides the delete operator. So, if you wanted to remove the gender property from the mikhail object:

1
2
3
4
5
delete mikhail['gender']
// => true

mikhail['gender']
// => undefined

The delete operator returns true if the property was removed, false otherwise. I won't delve into details of the workings of this operator, since @kangax has already written a most awesome article on how delete works.

1.7. Getters and setters

Getters and setters are usually used in classical object oriented languages to provide encapsulation. They are not much needed in JavaScript, though, given how dynamic the language is — and my bias against the feature.

At any rate, they allow you to proxy the requests for reading a property value or setting it, and decide how to handle each situation. So, suppose we had separate slots for our object's first and last name, but wanted a simple interface for reading and setting it.

First, let's set the first and last names of our friend, as concrete data properties:

1
2
3
4
5
Object.defineProperty(mikhail, 'first_name', { value:    'Mikhail'
                                             , writable: true })

Object.defineProperty(mikhail, 'last_name', { value:    'Weiß'
                                            , writable: true })

Then we can define a common way of accessing and setting both of those values at the same time — let's call it name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// () → String
// Returns the full name of object.
function get_full_name() {
    return this.first_name + ' ' + this.last_name
}

// (new_name:String) → undefined
// Sets the name components of the object, from a full name.
function set_full_name(new_name) { var names
    names = new_name.trim().split(/\s+/)
    this.first_name = names['0'] || ''
    this.last_name  = names['1'] || ''
}

Object.defineProperty(mikhail, 'name', { get: get_full_name
                                       , set: set_full_name
                                       , configurable: true
                                       , enumerable:   true })

Now, every-time we try to access the value of Mikhail's name property, it'll execute the get_full_name getter.

1
2
3
4
5
6
7
8
9
10
11
12
mikhail.name
// => 'Mikhail Weiß'

mikhail.first_name
// => 'Mikhail'

mikhail.last_name
// => 'Weiß'

mikhail.last_name = 'White'
mikhail.name
// => 'Mikhail White'

We can also set the name of the object, by assigning a value to the property, this will then execute set_full_name to do the dirty work.

1
2
3
4
5
6
7
8
9
10
mikhail.name = 'Michael White'

mikhail.name
// => 'Michael White'

mikhail.first_name
// => 'Michael'

mikhail.last_name
// => 'White'

Of course, getters and setters make property access and modification fairly slower. They do have some use-cases, but while browsers don't optimise them better, methods seem to be the way to go.

Also, it should be noted that while getters and setters are usually used for encapsulation in other languages, in ECMAScript 5 you still can't have such if you need the information to be stored in the object itself. All properties in an object are public.

1.8. Listing properties

Since properties are dynamic, JavaScript provides a way of checking out which properties an object defines. There are two ways of listing the properties of an object, depending on what kind of properties one is interested into.

The first one is done through a call to Object.getOwnPropertyNames, which returns an Array containing the names of all properties set directly in the object — we call these kind of property own, by the way.

If we check now what we know about Mikhail:

1
2
Object.getOwnPropertyNames(mikhail)
// => [ 'name', 'age', 'gender', 'first_name', 'last_name' ]

The second way is using Object.keys, which returns all own properties that have been marked as enumerable when they were defined:

1
2
Object.keys(mikhail)
// => [ 'name', 'age', 'gender' ]

1.9. Object literals

An even easier way of defining objects is to use the object literal (also called object initialiser) syntax that JavaScript provides. An object literal denotes a fresh object, that has its parent as the Object.prototype object. We'll talk more about parents when we visit inheritance, later on.

At any rate, the object literal syntax allows you to define simple objects and initialise it with properties at the same time. So, we could rewrite our Mikhail object to the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var mikhail = { first_name: 'Mikhail'
              , last_name:  'Weiß'
              , age:        19
              , gender:     'Male'

              // () → String
              // Returns the full name of object.
              , get name() {
                    return this.first_name + ' ' + this.last_name }

              // (new_name:String) → undefined
              // Sets the name components of the object,
              // from a full name.
              , set name(new_name) { var names
                    names = new_name.trim().split(/\s+/)
                    this.first_name = names['0'] || ''
                    this.last_name  = names['1'] || '' }
              }

Property names that are not valid identifiers must be quoted. Also note that the getter/setter notation for object literals strictly defines a new anonymous function. If you want to assign a previously declared function to a getter/setter, you need to use the Object.defineProperty function.

The rules for object literal can be described as the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<object-literal>  ::= "{" <property-list> "}"
                    ;
<property-list>   ::= <property> ["," <property>]*
                    ;
<property>        ::= <data-property>
                    | <getter-property>
                    | <setter-property>
                    ;
<data-property>   ::= <property-name> ":" <expression>
                    ;
<getter-property> ::= "get" <identifier>
                    :       <function-parameters>
                    :       <function-block>
                    ;
<setter-property> ::= "set" <identifier>
                    :       <function-parameters>
                    :       <function-block>
                    ;
<property-name>   ::= <identifier>
                    | <quoted-identifier>
                    ;

Object literals can only appear inside expressions in JavaScript. Since the syntax is ambiguous to block statements in the language, new-comers usually confound the two:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// This is a block statement, with a label:
{ foo: 'bar' }
// => 'bar'

// This is a syntax error (labels can't be quoted):
{ "foo": 'bar' }
// => SyntaxError: Invalid label

// This is an object literal (note the parenthesis to force
// parsing the contents as an expression):
({ "foo": 'bar' })
// => { foo: 'bar' }

// Where the parser is already expecting expressions,
// object literals don't need to be forced. E.g.:
var x = { foo: 'bar' }
fn({foo: 'bar'})
return { foo: 'bar' }
1, { foo: 'bar' }
( ... )

2. Methods

Up until now, the Mikhail object only defined slots of concrete data — with the exception of the name getter/setter. Defining actions that may be performed on a certain object in JavaScript is just as simple.

This is because JavaScript does not differentiate how you can manipulate a Function, a Number or an Object. Everything is treated the same way (i.e.: functions in JavaScript are first-class).

As such, to define an action for a given object, you just assign a function object reference to a property. Let's say we wanted a way for Mikhail to greet someone:

1
2
3
4
5
// (person:String) → String
// Greets a random person
mikhail.greet = function(person) {
    return this.name + ': Why, hello there, ' + person + '.'
}

After setting the property, we can use it the same way we used the concrete data that were assigned to the object. That is, accessing the property will return a reference to the function object stored there, so we can just call.

1
2
3
4
5
mikhail.greet('you')
// => 'Michael White: Why, hello there, you.'

mikhail.greet('Kristin')
// => 'Michael White: Why, hello there, Kristin.'

2.1. Dynamic this

One thing that you must have noticed both in the greet function, and the functions we've used for the name's getter/setter, is that they use a magical variable called this.

It holds a reference to the object that the function is being applied to. This doesn't necessarily means that this will equal the object where the function is stored. No, JavaScript is not so selfish.

Functions are generics. That is, in JavaScript, what this refers to is decided dynamically, at the time the function is called, and depending only on how such a function is called.

Having this dynamically resolved is an incredible powerful mechanism for the dynamism of JavaScript's object orientation and lack of strictly enforced structures (i.e.: classes), this means one can apply a function to any object that meets the requirements of the actions it performs, regardless of how the object has been constructed — hack in some custom multiple dispatcher and you have CLOS.

2.2. How this is resolved

There are four different ways of resolving the this variable in a function, depending on how a function is called: directly; as a method; explicitly applied; as a constructor. We'll dive in the first three for now, and come back at constructors later on.

For the following examples, we'll take these definitions into account:

1
2
3
4
5
6
7
8
// (other:Number[, yet_another:Number]) → Number
// Returns the sum of the object's value with the given Number
function add(other, yet_another) {
    return this.value + other + (yet_another || 0)
}

var one = { value: 1, add: add }
var two = { value: 2, add: add }

2.2.1. Called as a method

If a function is called as an object's method, then this inside the function will refer to the object. That is, when we explicitly state that an object is carrying an action, then that object will be our this inside the function.

This is what happened when we called mikhail.greet(). The property access at the time of the call tells JavaScript that we want to apply whatever actions the greet function defines to the mikhail object.

1
2
3
4
5
6
7
8
one.add(two.value) // this === one
// => 3

two.add(3)         // this === two
// => 5

one['add'](two.value) // brackets are cool too
// => 3

2.2.2. Called directly

When a function is called directly, this will be resolved to the global object in the engine (e.g.: window in browsers, global in Node.js)

1
2
3
4
5
6
7
add(two.value)  // this === global
// => NaN

// The global object still has no `value' property, let's fix that.
value = 2
add(two.value)  // this === global
// => 4

2.2.3. Explicitly applied

Finally, a function may be explicitly applied to any object, regardless of whether the object has the function stored as a property or not. These applications are done through a either the call or apply method of a function object.

The difference between these two methods is the way they take in the parameters that will be passed to the function, and the performance — apply being up to 55x slower than a direct call, whereas call is usually not as bad. This might vary greatly depending on the engine though, so it's always better to do a Perf test rather than being scared of using the functionality — don't optimise early!

Anyways, call expects the object that the function will be applied to as the first parameter, and the parameters to apply to the function as positional arguments:

1
2
3
4
5
6
7
8
add.call(two, 2, 2)      // this === two
// => 6

add.call(window, 4)      // this === global
// => 6

add.call(one, one.value) // this === one
// => 2

On the other hand, apply lets you pass an array of parameters as the second parameter of the function. The array will be passed as positional arguments to the target function:

1
2
3
4
5
6
7
8
add.apply(two, [2, 2])       // equivalent to two.add(2, 2)
// => 6

add.apply(window, [ 4 ])       // equivalent to add(4)
// => 6

add.apply(one, [one.value])  // equivalent to one.add(one.value)
// => 2
Note

What this resolves to when applying a function to null or undefined depends on the semantics used by the engine. Usually, it would be the same as explicitly applying the function to the global object. But if the engine is running on strict mode, then this will be resolved as expected — to the exact thing it was applied to:

1
2
3
4
5
6
7
8
9
10
window.value = 2
add.call(undefined, 1) // this === window
// => 3

void function() {
  "use strict"
  add.call(undefined, 1) // this === undefined
  // => NaN
  // Since primitives can't hold properties.
}()

2.3. Bound methods

Aside from the dynamic nature of functions in JavaScript, there is also a way of making a function bound to an specific object, such that this inside that function will always resolve to the given object, regardless of whether it's called as that object's method or directly.

The function that provides such functionality is bind. It takes an object, and additional parameters (in the same manner as call), and returns a new function that will apply those parameters to the original function when called:

1
2
3
4
5
6
7
8
9
10
11
var one_add = add.bind(one)

one_add(2) // this === one
// => 3

two.one_adder = one_add
two.one_adder(2) // this === one
// => 3

one_add.call(two, 2) // this === one
// => 3

3. Inheritance

Up to this point we have seen how objects can define their own behaviours, and how we can reuse (by explicit application) actions in other objects, however, this still doesn't give us a nice way for code reuse and extensibility.

That's where inheritance comes into play. Inheritance allows for a greater separation of concerns, where objects define specialised behaviours by building upon the behaviours of other objects.

The prototypical model goes further than that, though, and allows for selective extensibility, behaviour sharing and other interesting patterns we'll explore in a bit. Sad thing is: the specific model of prototypical OO implemented by JavaScript is a bit limited, so circumventing these limitations to accommodate these patterns will bring in a bit of overhead sometimes.

3.1. Prototypes

Inheritance in JavaScript revolves around cloning the behaviours of an object and extending it with specialised behaviours. The object that has it's behaviours cloned is called Prototype (not to be confounded with the prototype property of functions).

A prototype is just a plain object, that happens to share it's behaviours with another object — it acts as the object's parent.

Now, the concepts of this behaviour cloning does not imply that you'll have two different copies of the same function, or data. In fact, JavaScript implements inheritance by delegation, all properties are kept in the parent, and access to them is just extended for the child.

As mentioned previously, the parent (or [⁣[Prototype]⁣]) of an object is defined by making a call to Object.create, and passing a reference of the object to use as parent in the first parameter.

This would come well in our example up until now. For example, the greeting and name actions can be well defined in a separate object and shared with other objects that need them.

Which takes us to the following model:

http://dl.dropbox.com/u/4429200/blog/oop-proto-person.png

We can implement this in JavaScript with the following definitions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var person = Object.create(null)

// Here we are reusing the previous getter/setter functions
Object.defineProperty(person, 'name', { get: get_full_name
                                      , set: set_full_name
                                      , configurable: true
                                      , enumerable:   true })

// And adding the `greet' function
person.greet = function (person) {
    return this.name + ': Why, hello there, ' + person + '.'
}

// Then we can share those behaviours with Mikhail
// By creating a new object that has it's [[Prototype]] property
// pointing to `person'.
var mikhail = Object.create(person)
mikhail.first_name = 'Mikhail'
mikhail.last_name  = 'Weiß'
mikhail.age        = 19
mikhail.gender     = 'Male'

// And we can test whether things are actually working.
// First, `name' should be looked on `person'
mikhail.name
// => 'Mikhail Weiß'

// Setting `name' should trigger the setter
mikhail.name = 'Michael White'

// Such that `first_name' and `last_name' now reflect the
// previously name setting.
mikhail.first_name
// => 'Michael'
mikhail.last_name
// => 'White'

// `greet' is also inherited from `person'.
mikhail.greet('you')
// => 'Michael White: Why, hello there, you.'

// And just to be sure, we can check which properties actually
// belong to `mikhail'
Object.keys(mikhail)
// => [ 'first_name', 'last_name', 'age', 'gender' ]

3.2. How [⁣[Prototype]⁣] works

As you could see from the previous example, none of the properties defined in Person have flown to the Mikhail object, and yet we could access them just fine. This happens because JavaScript implements delegated property access, that is, a property is searched through all parents of an object.

This parent chain is defined by a hidden slot in every object, called [⁣[Prototype]⁣]. You can't change this slot directly4, so the only way of setting it is when you're creating a fresh object.

When a property is requested from the object, the engine first tries to retrieve the property from the target object. If the property isn't there, the search continue through the immediate parent of that object, and the parent of that parent, and so on.

This means that we can change the behaviours of a prototype at run time, and have it reflected in all objects that inherit from it. For example, let's suppose we wanted a different default greeting:

1
2
3
4
5
6
7
8
// (person:String) → String
// Greets the given person
person.greet = function(person) {
    return this.name + ': Harro, ' + person + '.'
}

mikhail.greet('you')
// => 'Michael White: Harro, you.'

3.3. Overriding properties

So, prototypes (that is, inheritance) are used for sharing data with other objects, and it does such in a pretty fast and memory-effective manner too, since you'll always have only one instance of a given piece of data lying around.

Now what if we want to add specialised behaviours, that build upon the data that was shared with the object? Well, we have seen before that objects define their own behaviours by means of properties, so specialised behaviours follow the same principle — you just assign a value to the relevant property.

To better demonstrate it, suppose Person implements only a general greeting, and everyone inheriting from Person define their own specialised and unique greetings. Also, let's add a new person to our case scenario, so to outline better how objects are extended:

http://dl.dropbox.com/u/4429200/blog/oop-extend.png

Note that both mikhail and kristin define their own version of greet. In this case, whenever we call the greet method on them they'll use their own version of that behaviour, instead of the one that was shared from person.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Here we set up the greeting for a generic person

// (person:String) → String
// Greets the given person, formally
person.greet = function(person) {
    return this.name + ': Hello, ' + (person || 'you')
}

// And a greeting for our protagonist, Mikhail

// (person:String) → String
// Greets the given person, like a bro
mikhail.greet = function(person) {
    return this.name + ': \'sup, ' + (person || 'dude')
}

// And define our new protagonist, Kristin
var kristin = Object.create(person)
kristin.first_name = 'Kristin'
kristin.last_name  = 'Weiß'
kristin.age        = 19
kristin.gender     = 'Female'

// Alongside with her specific greeting manners

// (person:String) → String
// Greets the given person, sweetly
kristin.greet = function(person) {
    return this.name + ': \'ello, ' + (person || 'sweetie')
}

// Finally, we test if everything works according to the expected

mikhail.greet(kristin.first_name)
// => 'Michael White: \'sup, Kristin'

mikhail.greet()
// => 'Michael White: \'sup, dude'

kristin.greet(mikhail.first_name)
// => 'Kristin Weiß: \'ello, Michael'

// And just so we check how cool this [[Prototype]] thing is,
// let's get Kristin back to the generic behaviour

delete kristin.greet
// => true

kristin.greet(mikhail.first_name)
// => 'Kristin Weiß: Hello, Michael'

3.4. Mixins

Prototypes allow for behaviour sharing in JavaScript, and although they are undeniably powerful, they aren't quite as powerful as they could be. For one, prototypes only allow that one object inherit from another single object, while extending those behaviours as they see fit.

However, this approach quickly kills interesting things like behaviour composition, where we could mix-and-match several objects into one, with all the advantages highlighted in the prototypical inheritance.

Multiple inheritance would also allow the usage of data-parents — objects that provide an example state that fulfils the requirements for a given behaviour. Default properties, if you will.

Luckily, since we can define behaviours directly on an object in JavaScript, we can work-around these issues by using mixins — and adding a little overhead at object's creation time.

So, what are mixins anyways? Well, they are parent-less objects. That is, they fully define their own behaviour, and are mostly designed to be incorporated in other objects (although you could use their methods directly).

Continuing with our little protagonists' scenario, let's extend it to add some capabilities to them. Let's say that every person can also be a pianist or a singer. A given person can have no such abilities, be just a pianist, just a singer or both. This is the kind of case where JavaScript's model of prototypical inheritance falls short, so we're going to cheat a little bit.

http://dl.dropbox.com/u/4429200/blog/oop-mixins.png

For mixins to work, we first need to have a way of combining different objects into a single one. JavaScript doesn't provide this out-of-the box, but we can easily make one by copying all own property descriptors, the ones defined directly in the object, rather than inherited, from one object to another.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Aliases for the rather verbose methods on ES5
var descriptor  = Object.getOwnPropertyDescriptor
  , properties  = Object.getOwnPropertyNames
  , define_prop = Object.defineProperty

// (target:Object, source:Object) → Object
// Copies properties from `source' to `target'
function extend(target, source) {
    properties(source).forEach(function(key) {
        define_prop(target, key, descriptor(source, key)) })

    return target
}

Basically, what extend does here is taking two objects — a source and a target, — iterating over all properties present on the source object, and copying the property descriptors over to target. Note that this is a destructive method, meaning that target will be modified in-place. It's the cheapest way, though, and usually not a problem.

Now that we have a method for copying properties over, we can start assigning multiple abilities to our objects (mikhail e kristin):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// A pianist is someone who can `play' the piano
var pianist = Object.create(null)
pianist.play = function() {
    return this.name + ' starts playing the piano.'
}

// A singer is someone who can `sing'
var singer = Object.create(null)
singer.sing = function() {
    return this.name + ' starts singing.'
}

// Then we can move on to adding those abilities to
// our main objects:
extend(mikhail, pianist)
mikhail.play()
// => 'Michael White starts playing the piano.'

// We can see that all that ends up as an own property of
// mikhail. It is not shared.
Object.keys(mikhail)
['first_name', 'last_name', 'age', 'gender', 'greet', 'play']

// Then we can define kristin as a singer
extend(kristin, singer)
kristin.sing()
// => 'Kristin Weiß starts singing.'

// Mikhail can't sing yet though
mikhail.sing()
// => TypeError: Object #<Object> has no method 'sing'

// But mikhail will inherit the `sing' method if we
// extend the Person prototype with it:
extend(person, singer)

mikhail.sing()
// => 'Michael White starts singing.'

3.5. Accessing overwritten properties

Now that we're able to inherit properties from other objects and extend the specialised objects to define their own behaviours, we have a little problem: what if we actually wanted to access the parent behaviours that we just overwrote?

JavaScript provides the Object.getPrototypeOf function, that returns the [⁣[Prototype]⁣] of an object. This way, we have access to all properties defined within the prototype chain of an object. So, accessing a property in the parent of an object is quite simple:

1
2
3
4
5
6
7
8
9
Object.getPrototypeOf(mikhail).name    // same as `person.name'
// => 'undefined undefined'

// We can assert it's really being called on `person' by
// giving `person' a `first_name' and `last_name'
person.first_name = 'Random'
person.last_name  = 'Person'
Object.getPrototypeOf(mikhail).name
// => 'Random Person'

So, a naïve solution for applying a method stored in the [⁣[Prototype]⁣] of an object to the current one, would then follow, quite naturally, by looking the property on the [⁣[Prototype]⁣] of this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var proto = Object.getPrototypeOf

// (name:String) → String
// Greets someone intimately if we know them, otherwise use
// the generic greeting
mikhail.greet = function(name) {
    return name == 'Kristin Weiß'?  this.name + ': Heya, Kristty'
         : /* we dunno this guy */  proto(this).greet.call(this, name)
}

mikhail.greet(kristin.name)
// => 'Michael White: Heya, Kristty'

mikhail.greet('Margareth')
// => 'Michael White: Hello, Margareth'

This looks all good and well, but there's a little catch: it will enter in endless recursion if you try to apply this approach to more than one parent. This happens because the methods are always applied in the context of the message's first target, making the [⁣[Prototype]⁣] lookup resolve always to the same object:

http://dl.dropbox.com/u/4429200/blog/oop-super.png

The simple solution to this, then, is to make all parent look-ups static, by passing the object where the current function is stored, rather than the object that the function was applied to.

So, the last example becomes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var proto = Object.getPrototypeOf

// (name:String) → String
// Greets someone intimately if we know them, otherwise use
// the generic greeting.
//
// Note that now we explicitly state that the lookup should take
// the parent of `mikhail', so we can be assured the cyclic parent
// resolution above won't happen.
mikhail.greet = function(name) {
    return name == 'Kristin Weiß'?  this.name + ': Heya, Kristty'
         : /* we dunno this guy */  proto(mikhail).greet.call(this, name)
}

mikhail.greet(kristin.name)
// => 'Michael White: Heya, Kristty'

mikhail.greet('Margareth')
// => 'Michael White: Hello, Margareth'

Still, this has quite some short-commings. First, since the object is hard-coded in the function, we can't just assign the function to any object and have it just work, as we did up 'till now. The function would always resolve to the parent of mikhail, not of the object where it's stored.

Likewise, we can't just apply the function to any object. The function is not generic anymore. Unfortunately, though, making the parent resolution dynamic would require us to pass an additional parameter to every function call, which is something that can't be achieved short of ugly hacks.

The approach proposed for the next version of JavaScript only solves the first problem, which is the easiest. Here, we'll do the same, by introducing a new way of defining methods. Yes, methods, not generic functions.

Functions that need to access the properties in the [⁣[Prototype]⁣] will require an additional information: the object where they are stored. This makes the lookup static, but solves our cyclic lookup problem.

We do this by introducing a new function — make_method — which creates a function that passes this information to the target function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// (object:Object, fun:Function) → Function
// Creates a method
function make_method(object, fun) {
    return function() { var args
        args = slice.call(arguments)
        args.unshift(object)        // insert `object' as first parameter
        fn.apply(this, args) }
}


// Now, all functions that are expected to be used as a method
// should remember to reserve the first parameter to the object
// where they're stored.
//
// Note that, however, this is a magical parameter introduced
// by the method function, so any function calling the method
// should pass only the usual arguments.
function message(self, message) { var parent
    parent = Object.getPrototypeOf(self)
    if (parent && parent.log)
        parent.log.call(this, message)

    console.log('-- At ' + self.name)
    console.log(this.name + ': ' + message)
}

// Here we define a prototype chain C -> B -> A
var A  = Object.create(null)
A.name = 'A'
A.log  = make_method(A, message)

var B  = Object.create(A)
B.name = 'B'
B.log  = make_method(B, message)

var C  = Object.create(B)
C.name = 'C'
C.log  = make_method(C, message)

// And we can test if it works by calling the methods:
A.log('foo')
// => '-- At A'
// => 'A: foo'

B.log('foo')
// => '-- At A'
// => 'B: foo'
// => '-- At B'
// => 'B: foo'

C.log('foo')
// => '-- At A'
// => 'C: foo'
// => '-- At B'
// => 'C: foo'
// => '-- At C'
// => 'C: foo'

4. Constructors

Constructor functions are the old pattern for creating objects in JavaScript, which couple inheritance with initialisation in an imperative manner.

Constructor functions are not, however, a special construct in the language. Any simple function can be used as a constructor function; just like this, it all depends on how the function is called.

So, what's it about constructor functions, really? Well, every function object in JavaScript automatically gets a prototype property, that is a simple object with a constructor property pointing back to the constructor function. And this object is used to determine the [⁣[Prototype]⁣] of instances created with that constructor function.

The following diagram shows the objects for the constructor function function Person(first_name, last_name):

http://dl.dropbox.com/u/4429200/blog/oop-ctor.png

4.1. The new magic

The prototype per se is not a special property, however it gains special meaning when a constructor function is used in conjunction with the new statement. As I said before, in this case the prototype property of the constructor function is used to provide the [⁣[Prototype]⁣] of the instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Constructs a new Person
function Person(first_name, last_name) {
    // If the function is called with `new', as we expect
    // `this' here will be the freshly created object
    // with the [[Prototype]] set to Person.prototype
    //
    // Of course, if someone omits new when calling the
    // function, the usual resolution of `this' — as
    // explained before — will take place.
    this.first_name = first_name
    this.last_name  = last_name
}

// (person:String) → String
// Greets the given person
Person.prototype.greet = function(person) {
    return this.name + ': Harro, ' + person + '.' 
}

var person = new Person('Mikhail', 'Weiß')


// We could de-sugar the constructor pattern in the following
// Taking into account that `Person' here means the `prototype'
// property of the `Person' constructor.
var Person = Object.create(Object.prototype)

// (person:String) → String
// Greets the given person
Person.greet = function(person) {
    return this.name + ': Harro, ' + person + '.' 
}

// Here's what the constructor does when called with `new'
var person = Object.create(Person)
person.first_name = 'Mikhail'
person.last_name  = 'Weiß'

When a function is called with the new statement, the following magic happens:

  1. Create a fresh Object, inheriting from Object.prototype, say { }
  2. Set the [⁣[Prototype]⁣] internal property of the new object to point to the constructor's prototype property, so it inherits those behaviours.
  3. Call the constructor in the context of this fresh object, such that this inside the constructor will be the fresh object, and pass any parameters given to the function.
  4. If the function returns an Object, make that be the return value of the function.
  5. Otherwise, return the fresh object.

This means that the resulting value of calling a function with the new operator is not necessarily the object that was created. A function is free to return any other Object value as it sees fit. This is an interesting and — to a certain extent — powerful behaviour, but also a confusing one for many newcomers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Foo() {
    this.foo = 'bar'
}

new Foo()
// => { foo: 'bar' }


function Foo() {
    this.foo = 'bar'
    return Foo
}

new Foo()
// => [Function: Foo]

4.2. Inheritance with constructors

We've covered inheritance with plain objects through Object.create, inheritance with constructors follow quite naturally from there, the difference being that instead of the main actor being the target of the inheritance (the constructor function, in this case), the prototype property is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// new Person (first_name:String, last_name:String)
// Initialises a Person object
function Person(first_name, last_name) {
    this.first_name = first_name
    this.last_name  = last_name
}

// Defines the `name' getter/setter
Object.defineProperty(Person.prototype, 'name', { get: get_full_name
                                                , set: set_full_name
                                                , configurable: true
                                                , enumerable:   true })

// (person:String) → String
// Greets the given person
Person.prototype.greet = function(person) {
    return this.name + ': Hello, ' + (person || 'you')
}


var proto = Object.getPrototypeOf

// new Mikhail (age:Number, gender:String)
function Mikhail(age, gender) {
    // Find the parent of this object and invoke its constructor
    // with the current this. We could have used:
    //   Person.call(this, 'Mikhail', 'Weiß')
    // But we'd lose some flexibility with that.
    proto(Mikhail.prototype).constructor.call(this, 'Mikhail', 'Weiß')
}

// Inherits the properties from Person.prototype
Mikhail.prototype = Object.create(Person.prototype)

// Resets the `constructor' property of the prototype object
Mikhail.prototype.constructor = Mikhail

// (person:String) → String
Mikhail.prototype.greet = function(person) {
    return this.name + ': \'sup, ' + (person || 'dude')
}


// Instances are created with the `new' operator, as previously
// discussed:
var mikhail = new Mikhail(19, 'Male')
mikhail.greet('Kristin')
// => 'Mikhail Weiß: \'sup, Kristin'

5. Considerations and compatibility

The functions and concepts presented up until now assumed that the code would be running in an ECMAScript 5 environment, since the new additions make prototypical inheritance more natural, without the initialisation and inheritance coupling provided by constructor functions.

However, obviously this means that code using these functions will not work everywhere. @kangax has a most awesome compatibility table for the implementations that follow ECMAScript 5.

This section provides fallbacks to some of the functionality, and point to libraries that implement these fallbacks so you don't get to reinvent the wheel. Note that this section only exists to highlight how the functionality works, and how the core part of those behaviours could be reproduced in legacy code, it's not meant to provide ready-to-use fallbacks. Use libraries for that :3

5.1. Creating objects

In ECMAScript 5 we have got Object.create to handle inheritance, but constructor functions can also be used to set the [⁣[Prototype]⁣] link for the constructed object — which is what we're interested about.

A clone function could be defined such that it would create a new object based on the given prototype:

1
2
3
4
5
6
7
8
9
10
11
12
13
// (proto:Object) → Object
// Constructs an object and sets the [[Prototype]] to `proto'.
function clone(proto) {
    function Dummy() { }

    Dummy.prototype             = proto
    Dummy.prototype.constructor = Dummy

    return new Dummy()
}

var mikhail = clone(person)
// Equivalent to `var mikhail = Object.create(person)'

5.2. Defining properties

Object.defineProperty and it's batch cousin Object.defineProperties are also new additions, and they allow properties to be defined with internal tags, like writable, configurable and enumerable. It's not possible to get this behaviour in the older versions of the language.

All properties defined otherwise will inevitable have writable, configurable and enumerable set to true, which is usually not really that much of a problem — still, not compatible with full ES5 code.

In regards of getters and setters, they are supported to a certain extent with non-standard syntax — the __defineGetter__ and __defineSetter__ methods, — but are also not available everywhere. Most notably, such methods have never been present in IE.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// (target:Object, key:String, descriptor:Object) → Object
// Defines a property in the target object.
// Getters and Setters are handled through the fallback
// calls, whereas values are set directly. Tags are
// ignored.
function defineProperty(target, key, descriptor) {
    if (descriptor.value)
        target[key] = descriptor.value
    else {
        descriptor.get && target.__defineGetter__(key, descriptor.get)
        descriptor.set && target.__defineSetter__(key, descriptor.set) }

    return target
}


var x = { }
defineProperty(x, 'foo', { value: 'bar' })
defineProperty(x, 'bar', { get: function() { return this.foo }
                         , set: function(v){ this.foo = v    }})

x.foo
// => 'bar'

x.bar
// => 'bar'

x.bar = 'foo'
x.foo
// => 'foo'

x.bar
// => 'foo'

5.3. Listing properties

We have seen how it's possible to list the properties of an object with Object.getOwnPropertyNames, and list only the enumerable properties through Object.keys. Well, prior to ECMAScript 5, listing the enumerable properties is the only thing one can do.

This is achieved through the for..in statement, which iterates through all the enumerable properties of an object, either directly set in the object, or in the prototype chain. Object.prototype.hasOwnProperty may be used to filter the properties to include only the ones set directly in the object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// (object:Object) → Array
// Lists all the own enumerable properties of an object
function keys(object) { var result, key
    result = []
    for (key in object)
        if (object.hasOwnProperty(key))  result.push(key)

    return result
}

// Taking the mikhail object whose [[Prototype]] is person...
keys(mikhail)
// => [ 'first_name', 'last_name', 'age', 'gender' ]

keys(person)
// => [ 'greet', 'name' ]

5.4. Bound methods

Bound methods in JavaScript do much more than just assert the value of this inside a function, they can also be used for partial function applications and behave slightly different when called as a constructor. For this, we'll just focus on the first two.

Basically, when calling the bind method of a function, we're creating a new function object that has a defined thisObject and perhaps a defined initial list of arguments. This can be just as well achieved with a closure to store the given state, and a explicit function application, through the apply method.

1
2
3
4
5
6
7
8
9
10
11
var slice = [].slice

// (fun:Function, bound_this:Object, args...) → Function
//  --> (args...) → *mixed*
// Creates a bound method from the function `fn'
function bind(fn, bound_this) { var bound_args
    bound_args = slice.call(arguments, 2)
    return function() { var args
        args = bound_args.concat(slice.call(arguments))
        return fn.apply(bound_this, args) }
}

5.5. Getting the [⁣[Prototype]⁣]

For accessing overriden properties, we need to get the a reference to the [⁣[Prototype]⁣]. In environments that expose such link (like Firefox's SpiderMonkey or Chrome's V8), it's easy and reliable:

1
2
3
4
function proto(object) {
    return object?            object.__proto__
         : /* not object? */  null
}

However, in environments that don't expose the [⁣[Prototype]⁣] link, things aren't quite as reliable. The only way of getting the prototype of an object, in this case, would be by the constructor's prototype property, but we can only access that from the object given the constructor property is kept intact.

A fallback covering most cases would look like this:

1
2
3
4
5
function proto(object) {
    return !object?                null
         : '__proto__' in object?  object.__proto__
         : /* not exposed? */      object.constructor.prototype
}

Note that the actual Object.getPrototypeOf throws a TypeError when you pass something that is not an object to it.

5.6. Libraries that provide fallbacks

ES5-shim attempts to implement fallbacks for ECMAScript 5 functionality that can be done in pure JavaScript, whilst adding minimal support for the other ones. It's important to note, however, that the fallbacks are intended to provide equivalent functionality that is close to the ones defined in the specs, it's not guaranteed that they will work exactly the same way.

To quote the README:

"As closely as possible to ES5" is not very close. Many of these shims are intended only to allow code to be written to ES5 without causing run-time errors in older engines. In many cases, this means that these shims cause many ES5 methods to silently fail. Decide carefully whether this is what you want.

6. Wrapping it up

The object orientation model chosen for JavaScript is definitely one of the things that makes the language expressive and powerful, however the really poor semantics from the before-ES5 age quite killed all the fun about it.

With ECMAScript 5, we have got better ways to deal with objects and inheritance, but most of the API is pretty verbose and awkward to use out of the box, so abstracting them is the only good way of exploring all the power of the first-class inheritance model provided by the language.

Once you dwell on the depths of JavaScript's prototypical object orientation, however, you will find it lacking on aspects that would otherwise seem like the obvious thing to do — like multiple inheritance and message resending, but also basic features like an easier object extension functionality.

Luckily most of these issues manage to have a solution, albeit not necessarily a satisfactory one in some cases — i.e.: manual mixins. Being able to reproduce semantics that are not provided straight away on the language by patterns leveraging the built-in constructs is an important part of the language, and this all is made easier because of the way functions are treated in JavaScript.

7. Things worth reading up next


Brendan Eich's "Prototypical vs Closure" rant

Although not really a reading, this particular podcast from Brendan Eich is a must listen for anyone working with object oriented JavaScript. it delves on the performance of engines in regards of object construction, highlighting how the prototypical pattern stands against the Closure pattern, and discussing the specifics of how browsers handle prototypical code so they run fast.

Organizing Programs Without Classes (paper)

Albeit not specific to JavaScript, this white-paper dwells on how the structuring of programs differ from the classical object orientation approach to the prototypical take on the subject. It provides lots of Self code to go with it, but they are more or less easily translated to JavaScript code.

8. Acknowledgements

Thanks to @hughfdjackson, Alex and Taylor for the additional revision of the article.

Thanks to -- for pointing out (in the comments) that properties expect an IdentifierName, rather than a plain Identifier, which would allow reserved words (like foo.class or foo.null) to be used unquoted.

Footnotes:

1 : Some implementations have magical names, like __proto__, which may yield undesired and unwanted results when set. For example, the __proto__ property is used to define the parent of an object in some implementations. As such, you wouldn't be able to set a string or number to that.

2 : While an IdentifierName also allows reserved words, you should be aware that ECMAScript engines that aren't fully compliant with the ECMAScript 5 specs may choke on reserved words when they're used for property access unquoted.

3 : It should be noted that, while ECMAScript-defined native objects don't throw an error when you try to access a non-existing property, it's not guaranteed that the same will hold true for a host object (i.e. an object defined by the engine implementor). After all, host object semantics are not defined, they are dependent on the particular run-time implementation.

4 Some engines do expose the [⁣[Prototype]⁣] slot, usually through a property like __proto__, however no such thing is described in the specifications for the language, so it's recommended that you avoid using it, unless you're well aware that all platforms you code must run on will have such means of setting the [⁣[Prototype]⁣] object directly. It should also be noted that messing with the prototype chain might defeat all look-up optimisations in the JS engine.