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:
- Check whether
this
is really aPerson
- which it would be if called usingnew
. - If it indeed is a
Person
, go on your merry way. - If it is not a
Person
, use anew
to create aPerson
- 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:
- An object has a number of properties. This includes any attributes or functions(methods).
- 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. - An object can override a property of its parent by setting the property on itself.
- A constructor creates objects. Each constructor has an associated
prototype
object, which is simply another object. - 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:
- Constructors
- Methods and Prototypes
apply
andcall
- Implementing the
new
method