Javascript Constructors and Prototypes

If you didn’t already know, Javascript functions double as object constructors. For example, to simulate a class in object-oriented programming, you would write

function Person(name){
    this.name = name
}

Note: I avoid using semicolons because I am a heretic! Mislav argues my point for me here.

Anyway, now that you have a function, you’d use the new statement to create a Person

var bob = new Person('Bob')
// {name: 'Bob'}

Just to be sure that bob is indeed a Person, we can ask

bob instanceof Person
// true

You could also call Person as a function - without the new,

Person('Bob')
// undefined

but that just returns undefined. Also, you really don’t want to do this, because you’ve just unintentionally created a name global variable

name
// 'Bob'

Uh… that’s not good, especially if I already had a variable called name, it would have been overwritten. The reason this happens is because when you call a function as a function(without new), this is set to the global object - in the browser, this is the window object, see

window.name
// 'Bob'
this === window
// true

So… if you want to write a constructor, use it like a constructor, if you want to write a function, use it like a function, don’t mix and match.

Someone pointed out though, that you can prevent this polluting of the namespace(those are just big words for creating global variables) by using this trick

function Person(name){
    if (!(this instanceof Person))
        return new Person(name)
    this.name = name
}

What this does is:

  1. Check whether this is really a Person - which it would be if called using new.
  2. If it indeed is a Person, go on your merry way.
  3. If it is not a Person, use a new to create a Person - the correct way, and return it.

This allows calling it as a function to return a Person, and it doesn’t pollute the namespace.

Person('Bob')
// {name: 'Bob'}
name
// undefined

But what is surprising is that calling it with new still works too

new Person('Bob')
// {name: 'Bob'}

Why? It turns out that if you return a value in a constructor, Javascript will honor it, and return it as the newly created object when you use a new. But, you might be thinking, can I return a non-Person? That would be kind of like lying.

function Cat(name){
    this.name = name
}
function Person(name){
    return new Cat(name)
}
var bob = new Person('Bob')
bob instanceof Person
// false
bob instanceof Cat
// true

So, I ask for a Person and I get a Cat? Well, in Javascript it can happen. You can even return an Array.

function Person(name){
    return [name]
}
new Person('Bob')
// ['Bob']

There are limits to this madness though: if you return a value of a primitive type, this won’t work.

function Person(name){
    this.name = name
    return 5
}
new Person('Bob')
// {name: 'Bob'}

Number, String, Boolean, are all primitive types. If you return one of these types of values from a constructor, it would be ignored and the constructor would go back to its normal behavior of returning the this object.

Methods

In the beginning, I said that functions double as constructors, well, actually, they more like triple. Functions also act as methods.

If you know OOP, you know that methods are the behaviors of the object - what the object can do. In Javascript, methods are just functions attached to an object - you can create methods simply by creating functions and assigning them to the object

function Person(name){
    this.name = name
    this.sayHi = function(){
        return 'Hi, I am ' + this.name
    }
}

Bob can now say “Hi”

var bob = new Person('Bob')
bob.sayHi()
// 'Hi, I am Bob'

Actually, we can attach methods to objects without even bothering with this constructor crap and instead create an object out right

var bob = {name: 'Bob'} // this is a Javascript object!
bob.sayHi = function(){
    return 'Hi, I am ' + this.name
}

This would work just as well. Or, if you prefer, write it as one big object

var bob = {
    name: 'Bob',
    sayHi: function(){
        return 'Hi, I am ' + this.name
    }
}

So, why are we bothering with constructors in the first place? Answer: inheritance.

Inheritance and the Prototype

Right, so inheritance. You know inheritance, right? You know how in Java, for example, you can have one class inherit another and automatically get all the methods and variables of the parent class?

public class Mammal{
    public void breathe(){
        // do some breathing
    }
}
public class Cat extends Mammal{
    // now cat too can breathe!
}

Well, in Javascript, we have the same thing, just different. For starters, we don’t even have classes! Instead, we have something called the prototype. Here’s how we write the equivalent of the above Java code in Javascript

function Mammal(){
}
Mammal.prototype.breathe = function(){
    // do some breathing
}
function Cat(){
}
Cat.prototype = new Mammal()
Cat.prototype.constructor = Cat
// now cat too can breathe!

What’s this prototype? That’s just a bunch of gibberish!

Javascript is different from traditional object-oriented languages in that it uses prototype inheritance. In a nutshell, prototype inheritance in Javascript works like this:

  1. An object has a number of properties. This includes any attributes or functions(methods).
  2. An object has a special parent property, this is also called the prototype of the object(__proto__). An object inherits all the properties of its parent.
  3. An object can override a property of its parent by setting the property on itself.
  4. A constructor creates objects. Each constructor has an associated prototype object, which is simply another object.
  5. When an object is created, it’s parent is set to the prototype object associated with the constructor that created it.

Okay! Now that you understand everything there is to know about prototype inheritance, let’s look at our Cat example more closely and break it down.

First, we create a constructor for Mammal

function Mammal(){
}

At this point, Mammal already has an associated prototype

Mammal.prototype
// {}

Let’s create an instance

var mammal = new Mammal()

Now, let’s verify the second law of prototype inheritence(it’s just the second bullet from the list)

mammal.__proto__ === Mammal.prototype
// true

Next, we add the breathe function to the prototype of Mammal

Mammal.prototype.breathe = function(){
    // do some breathing
}

At this point, mammal the instance can breathe

mammal.breathe()

because it inherits from Mammal.prototype. Next,

function Cat(){
}
Cat.prototype = new Mammal()

Cat constructor is created and we set Cat.prototype to a new instance of Mammal. Why do we do this?

var garfield = new Cat()
garfield.breathe()

because now any cat instance inherits Mammal and will therefore be able to breathe as well. Next,

Cat.prototype.constructor = Cat

Ensures that cats know that they are cats:

garfield.__proto__ === Cat.prototype
// true
Cat.prototype.constructor === Cat
// true
garfield instanceof Cat
// true

Each time you create a new instance of Cat, you create a 2-level chain, in that garfield is now parented by Cat.prototype which, since it is an instance of Mammal, is in turn parented by Mammal.prototype.

Now, guess who’s the parent of Mammal.prototype? Yeah, you guessed it, Object.prototype. So actually, it’s a 3-level chain

garfield -> Cat.prototype -> Mammal.prototype -> Object.prototype

You can add properties to any of garfield’s parents, and garfield would magically gain those properties too, even after garfield has already been created!

Cat.prototype.isCat = true
Mammal.prototype.isMammal = true
Object.prototype.isObject = true
garfield.isCat // true
garfield.isMammal // true
garfield.isObject // true

You can ask whether he has a given property

'isMammal' in garfield
// true

and you can also distinguish between own properties vs inherited properties

garfield.name = 'Garfield'
garfield.hasOwnProperty('name')
// true
garfield.hasOwnProperty('breathe')
// false

Setting Methods on the Prototype

Now that you really understand prototypes, let’s go back to the very first example of defining methods on objects

function Person(name){
    this.name = name
    this.sayHi = function(){
        return 'Hi, I am ' + this.name
    }
}

This is actually not the optimal way to do it. A better way is to define the method on Person.prototype

function Person(name){
    this.name = name
}
Person.prototype.sayHi = function(){
    return 'Hi, I am ' + this.name
}

Why is this better? Anyone? Anyone? Beuller?

In the first version, each time you create a person, a new sayHi function will be created for him, where as in the second version, only one sayHi function is ever created, and is shared amongst all persons that are created - because Person.prototype is their parent. Thus, declaring methods on the prototype is more memory efficient.

Apply and Call

As you can see, functions become methods just by virtue of being attached to objects, at which point the this within that function refers to the object which it is attached to, right? Well… not exactly. Look at our previous example

function Person(name){
    this.name = name
}
Person.prototype.sayHi = function(){
    return 'Hi, I am ' + this.name
}

Now, if you create 2 people, jack and jill

var jack = new Person('Jack')
var jill = new Person('Jill')
jack.sayHi()
// 'Hi, I am Jack'
jill.sayHi()
// 'Hi, I am Jill'

Here, sayHi is not attached to jack or jill, rather, it’s attached to their prototype: Person.prototype. How does the function sayHi know jack and jill’s names?

Answer: this is not bound to any particular object until you call the function.

When you call jack.sayHi(), sayHi’s this will be bound to jack; when you call jill.sayHi(), it will be bound to jill instead, but binding does not change anything about the function itself - it’s still the same function!

It turns out that you can explicitly bind a function to an object yourself.

function sing(){
    return this.name + ' sings!'
}
sing.apply(jack)
// 'Jack sings!'

The apply method belongs to Function.prototype(yeah, that’s right, functions are objects and have prototypes too and can also have properties!). So, you can use apply with any function to call it while binding it to the object of your choosing, even if the function is not attached to it. In fact, you can even apply the method to an object of a different type

function Flower(name){
    this.name = name
}
var tulip = new Flower('Tulip')
jack.sayHi.apply(tulip)
// 'Hi, I am Tulip'

You might say

Wait a minute! A Tulip is not supposed to say hi!

To that, I would say

Everything is everybody. Everybody is everything. We all cool! Just…chill, man!

As long as the object has a name property, sayHi is happy to print it out. This is the principle of duck typing

If it quacks like a duck and it walks like a duck - it’s a duck to me.

I am sure I misquoted that, but whatever.

Now back to the apply function: if you want to include parameters you can pass them as an array as the second parameter to apply.

function singTo(other){
    return this.name + ' sings for ' + other.name
}
singTo.apply(jack, [jill])
// 'Jack sings for Jill'

Function.prototype also has a call function, which works very much like apply. The only difference is in that rather than passing the parameters as an array in the second parameter, you would just add them to the end:

sing.call(jack, jill)
// 'Jack sings for Jill'

The new method

Now, for something fun

apply is really handy for certain situations when you want to call a function with a variable list of arguments. For example, the Math.max function takes a variable number of arguments

Math.max(4, 1, 8, 9, 2)
// 9

This is nice, but it’s not generic. By using apply you can get the max for an arbitrary array,

Math.max.apply(Math, myarray)

Much more useful!

Now, given that apply is so useful, there may come times when you want to use it, but rather than call-as-function,

Math.max.apply(Math, args)

you want to call-as-constructor.

new Person.apply(Person, args)

Sadly, this doesn’t work. It’ll think you are calling Person.apply as a constructor. How about this

(new Person).apply(Person, args)

That doesn’t work either, because it will first construct a person, and then try calling the apply method on that person.

What to do? Thanks to an idea presented on this answer on StackOverflow, there is a way!

We can create a new method for Function.prototype.

Function.prototype.new = function(){
    var args = arguments
    var constructor = this
    function Fake(){
         constructor.apply(this, args)
    }
    Fake.prototype = constructor.prototype
    return new Fake
}

With this, we can call constructors with the new method rather than the new statement

var bob = Person.new('Bob')

Let’s go through how the new method works.

First,

var args = arguments
var constructor = this
function Fake(){
     constructor.apply(this, args)
}

we create a Fake constructor which will apply our real constructor as a method when created. In the context of the new method, this is the real constructor - we save it to be used in the Fake constructor. We also save the arguments with which new was called to reuse in the Fake constructor. Next,

Fake.prototype = constructor.prototype

we set Fake.prototype to the original constructor. Since the prototype’s constructor property is still set to the original constructor, any object created by Fake will still be an instanceof the original constructor. Finally,

return new Fake

Create the object using the Fake constructor and return it.

Did you get all that? It’s okay if you don’t get it the first time; just look it over and poke at it a few more times.

Anyways, the point of all of that was that now you can do things like

var children = [new Person('Ben'), new Person('Dan')]
var args = ['Bob'].concat(children)
var bob = Person.new.apply(Person, args)

Nice! But why do we have to write Person twice? We can probably write a helper method

Function.prototype.applyNew = function(){
     return this.new.apply(this, arguments)
}

So you can do

var bob = Person.applyNew(args)

Nice, slightly better!

What’s the point of this exercise anyway?

Well, it shows that Javascript is a flexible little language. Even if it doesn’t do the things you want, you can probably mold it into doing them.

Summary

This is the end of this lesson/article/blog post. It was nice having you! Today we learned about:

  1. Constructors
  2. Methods and Prototypes
  3. apply and call
  4. Implementing the new method
blog comments powered by Disqus