This chapter discusses the object-oriented features of JavaScript, including objects, constructor functions, and prototypes. It also talks about code reuse and inheritance.
In PHP, if you have a Dog
class, you create a $fido
instance of this class using:
// PHP
$fido
=
new
Dog
();
JavaScript has a similar syntax:
// JavaScript
var
fido
=
new
Dog
();
One important difference is that Dog
is not a class in JavaScript because there are no classes in the language. Dog
is just a function. But functions that are meant to create objects are called constructor functions.
Syntactically, there is no difference between a regular function and a constructor function. The difference is in the intent. Thus, for readability purposes, it’s a common convention to capitalize the first letter in constructor function names.
When you call a function with the new
operator, it always returns an object. The object is known as this
inside the body of the function. That’s what happens even if you don’t do anything special in the function. Remember that otherwise (when called without new
) every function without an explicit return
returns undefined
:
function
Dog
()
{
this
.
name
=
"Fido"
;
this
.
sayName
=
function
()
{
return
"Woof! "
+
this
.
name
;
};
}
var
fido
=
new
Dog
();
fido
.
sayName
();
// "Woof! Fido"
Note
In JavaScript, just as in PHP, parentheses are optional when you’re not passing arguments to the constructor, so this is also valid: var fido = new Dog;
If you type fido
in the console, you’ll see that it has two properties: name
and sayName
. Some consoles also show you a special property called __proto__
, but you can ignore it for now.
Take a look at the sayName
property. It points to a function. Since functions are objects in JavaScript, they can be assigned to properties, in which case you can also call them methods. There’s really no difference between properties and methods in JavaScript. Methods are just callable properties.
When you call any function with new
, the following happens:
An “empty” object referenced via
this
is created automatically behind the scenes:var
this
=
{};
// Pseudocode, would be a syntax error if you use it
The programmer adds properties to
this
at will:this
.
name
=
"Fido"
;
this
is implicitly returned at the end of the function:return
this
;
// Not an error, but you don't need to use it
The programmer can interfere with step 3 by returning a different object:
function
Dog
()
{
var
notthis
=
{
noname
:
"Anonymous"
};
this
.
name
=
"Fido"
;
return
notthis
;
}
var
fido
=
new
Dog
();
fido
.
name
;
// undefined
fido
.
noname
;
// "Anonymous"
In this example, whatever you add to this
is simply destroyed when the function returns. You might as well remove it, in which case you don’t really need the magic provided by new
and you can call the function as a regular function and achieve the same effect:
function
Dog
()
{
return
{
noname
:
"Anonymous"
};
}
var
fido
=
Dog
();
// No `new` but `()` is needed this time
fido
.
name
;
// undefined
fido
.
noname
;
// "Anonymous"
Note, however, that returning something other than this
causes the instanceof
operator and the constructor
property to not work as expected:
function
Dog
()
{
return
{
noname
:
"Anonymous"
};
}
var
fido
=
new
Dog
();
fido
instanceof
Dog
;
// false
fido
.
constructor
===
Object
;
// true
When you use new
you can return a custom object (not this
), but it has to be an object. Trying to return a nonobject (a scalar value) results in your return value being ignored, and you still get this
at the end:
function
Dog
()
{
this
.
name
=
"Fido"
;
return
1
;
}
var
fido
=
new
Dog
();
typeof
fido
;
// "object"
fido
.
name
;
// "Fido"
As you know now, there’s no difference between constructor functions and regular functions other than their usage intent. So what happens if you add properties to this
in a nonconstructor function? In other words, what happens when you call a constructor function (which adds to this
) and forget to call it with new
?
function
Dog
()
{
this
.
thisIsTheName
=
"Fido"
;
return
1
;
}
var
fido
=
new
Dog
();
var
one
=
Dog
();
// fido is a regular object:
typeof
fido
;
// "object"
fido
.
thisIsTheName
;
// "Fido"
// one is 1, as returned from a nonconstructor function
typeof
one
;
// "number"
one
.
thisIsTheName
;
// undefined
// What?!
thisIsTheName
;
// "Fido"
The surprise here is that by calling Dog()
without new
, the global variable thisIsTheName
gets created. That’s because skipping new
means this is a regular function invocation and now this
refers to the global object. And properties added to the global object can be used as global variables. (There’s more about the global object later.)
This is dangerous behavior because you don’t want to pollute the global namespace. That’s why it’s important to use new
when it’s meant to be used. It’s also important to follow the convention of naming constructors with a capital letter so you send a hint about their intended purpose to the reader of the code:
function
Request
()
{}
// Ahaa!, it's a constructor
function
request
()
{}
// Just a function
If you are extra paranoid, you can make sure programmatically that your constructors always behave as constructors even when the callers forget new
. You can use the instanceof
operator, which takes an object and a constructor function reference and returns true
or false
:
fido
instanceof
Dog
;
// true
Here’s one way to implement the self-enforcing constructor:
function
Dog
()
{
// Check if the caller forgot `new`
if
(
!
(
this
instanceof
Dog
))
{
return
new
Dog
();
// Re-call properly
}
// Real work starts...
this
.
thisIsTheName
=
"Fido"
;
// real work ends
// Implicitly at the end ...
// return this;
}
var
newfido
=
new
Dog
();
var
fido
=
Dog
();
newfido
.
thisIsTheName
;
// "Fido"
fido
.
thisIsTheName
;
// "Fido"
The line this instanceof Dog
checks if the newly created this
object was created by Dog
. The line var fido = Dog();
didn’t use new
, so this
points to the global object. The global object was definitely not created by Dog
. After all, it was around even before Dog
, so the check fails and the line return new Dog();
is reached.
Note
You don’t really know exactly which constructor was used to create the global object because it’s an internal implementation dependent on the environment.
Another way to introspect and ask “Who created you?” is to use the constructor
property that all objects have. It’s also a writable property, so it’s not really reliable, as the following example shows:
function
Dog
()
{}
var
fido
=
new
Dog
();
fido
.
constructor
===
Dog
;
// true, as expected
fido
.
constructor
=
"I like potatoes"
;
fido
.
constructor
===
Dog
;
// false, ... wait, what?!
fido
.
constructor
;
// "I like potatoes"
The concept of prototypes doesn’t exist in PHP but is an important concept in JavaScript.
Let’s take a look at an example:
function
Dog
(
name
)
{
// Constructor
this
.
name
=
name
;
}
// Add a member to the `prototype` property
Dog
.
prototype
.
sayName
=
function
()
{
return
this
.
name
;
};
var
fido
=
new
Dog
(
"Fluffy"
);
fido
.
sayName
();
// "Fluffy"
What happened here?
-
There’s a normal function
Dog
, obviously created with the intent to be a constructor function because it starts with a capitalD
and refers tothis
inside its body. -
Behind the scenes, the
Dog()
function, like any other function, automatically gets a property calledprototype
. (You know that functions are objects, so they can have properties.) Theprototype
property is always created for each function, constructor or otherwise. -
You add a new property to the
prototype
property, calledsayName
. This property happens to be a function, so you can say it’s a method. -
sayName()
has access tothis
. -
Creating a
fido
object withnew Dog()
givesfido
access to all the properties added to theprototype
property. Otherwise, if you callDog()
withoutnew
, theprototype
and everything in it is ignored. -
fido.sayName()
works fine even thoughsayName
is not a property of thefido
object.
An object can access properties and methods that don’t belong to it but instead to the object referred to as prototype
of the constructor function that created the object.
Let this sink in a bit; there’s more on prototypes coming up.
You’ve already seen the use of object literals at several occasions in this book (e.g., when talking about representing PHP’s associative arrays in JavaScript).
Object literals are simply key-value pairs, delimited with commas and wrapped in curly braces:
var
obj
=
{
name
:
"Fluffy"
,
legs
:
4
,
tail
:
1
};
Note
It’s not OK to leave the trailing comma after the last property because some environments (earlier Internet Explorer versions) cannot handle it and raise an error.
You can also start with some properties (or no properties at all) and add more later:
var
obj
=
{};
obj
.
name
=
"Fluffy"
;
obj
.
legs
=
4
;
obj
.
tail
=
1
;
Having created an object using the object literal notation, you can access the properties using the dot notation:
var
desc
=
obj
.
name
+
" has "
+
obj
.
legs
+
" legs and "
+
obj
.
tail
+
" tail(s)"
;
desc
;
// "Fluffy has 4 legs and 1 tail(s)"
Alternatively, you can use the square bracket notation to access properties:
var
desc
=
obj
[
"name"
]
+
" has "
+
obj
[
"legs"
]
+
" legs and "
+
obj
[
"tail"
]
+
" tail(s)"
;
desc
;
// "Fluffy has 4 legs and 1 tail(s)"
This is not too common, because it’s longer and a little clumsy to be passing property names as strings. However, it is useful when you don’t know the property name in advance and need a variable. For example, when iterating over all properties:
var
all
=
[];
for
(
var
property
in
obj
)
{
all
.
push
(
property
+
": "
+
obj
[
property
]);
}
var
desc
=
all
.
join
(
', '
);
desc
;
// "name: Fluffy, legs: 4, tail: 1"
Or, as another example, when the property name is evaluated at runtime:
var
obj
=
{
foo
:
"Foo"
,
bar
:
"Bar"
,
foobar
:
"Foo + Bar = BFF"
};
var
fprop
=
"foo"
,
bprop
=
"bar"
;
obj
[
fprop
];
// "Foo"
obj
[
bprop
];
// "Bar"
obj
[
fprop
+
bprop
];
// "Foo + Bar = BFF"
Square bracket notation is required when the property is not a valid identifier (same for quotes around property names in an object literal):
var
fido
=
{};
fido
.
number
-
of
-
paws
=
4
;
// ReferenceError
fido
[
'number-of-paws'
]
=
4
;
// This is OK
Dots in JavaScript are used to access properties, but in PHP, they concatenate strings. When you’re mentally in PHP mode but you’re writing in JavaScript, you can often confuse the purpose of the dot out of habit:
// JavaScript
var
world
=
"wrrrld"
;
var
result
=
"hello "
.
world
;
Funny enough, this is not a syntax error in JavaScript. The result
contains the value undefined
. This is because the space around the dot is optional, so it works like this:
"hello"
.
world
;
// undefined
In other words, you’re accessing the property world
of the string object "hello"
. String literals are converted to objects behind the scenes, as if you did new String("hello")
(more about this coming soon). Since this object doesn’t have such a property, the result is undefined
.
Can you add methods to an object using object literal notation? Absolutely—methods are just properties, which happen to point to function objects:
var
obj
=
{
name
:
"Fluffy"
,
legs
:
4
,
tail
:
1
,
getDescription
:
function
()
{
return
obj
.
name
+
" has "
+
obj
.
legs
+
" legs and "
+
obj
.
tail
+
" tail(s)"
;
}
};
obj
.
getDescription
();
// "Fluffy has 4 legs and 1 tail(s)"
Note
In getDescription()
, you can substitute obj
with this
.
You can add methods to an existing object at a later time:
obj
.
getAllProps
=
function
()
{
var
all
=
[];
for
(
var
property
in
obj
)
{
if
(
typeof
obj
[
property
]
!==
"function"
)
{
all
.
push
(
property
+
": "
+
obj
[
property
]);
}
}
return
all
.
join
(
', '
);
};
obj
.
getAllProps
();
// "name: Fluffy, legs: 4, tail: 1"
Did you notice some properties were filtered out using the following?
typeof
obj
[
property
]
!==
"function"
Most likely, you don’t want functions showing up in the list of properties, but because functions are just like all other properties, they will show up if you don’t filter them out. You can try removing this filter to see what happens.
Note
Spoiler alert: accessing a function object in a string concatenation context converts the function object to a string. This happens by calling the toString()
method, which all objects that inherit Object
respond to. Function objects implement toString()
by returning the source code of the function, although this is not standard and implementations vary among engines in terms of new lines and spacing.
Overall, the obj
now looks like a fancy array, or like a PHP associative array that has some of its properties acting as functions. In fact, early PHP versions didn’t have the concept of objects at all. When objects were added later, they were dubbed fancy arrays. To this day, it’s easy to go back and forth between an object and an associative array in PHP:
// PHP
$mutt
=
array
(
'name'
=>
"Fluffy"
,
'legs'
=>
4
,
'tail'
=>
1
,
);
echo
$mutt
[
'name'
];
// "Fluffy"
var_dump
(
$mutt
->
name
);
// NULL, this is not an object
Here $mutt
is an array, so accessing name
as a property doesn’t work. However, you can convert $mutt
to an object:
// PHP
$mutt
=
(
object
)
$mutt
;
// $mutt is now an object
echo
$mutt
[
'name'
];
// Fatal error, this is not an array anymore
echo
$mutt
->
name
;
// "Fluffy"
As you can see, associative arrays and objects are so close that JavaScript decided to go with using only objects to express both concepts.
To continue the PHP/JavaScript analogy, you can imagine JavaScript’s object literals as being associative arrays converted to objects behind the scenes. Just as if you do the following in PHP:
// PHP
$mutt
=
(
object
)
array
(
"name"
=>
"Fluffy"
);
echo
$mutt
->
name
;
// "Fluffy"
// JavaScript
var
mutt
=
{
name
:
"Fluffy"
};
mutt
.
name
;
// "Fluffy"
In JavaScript, there is a distinction between properties owned by an object and properties that are inherited from a prototype
object. The syntax for accessing either of those is the same, but sometimes you need to know if a property belongs to your object or if it came from someplace else.
Own properties are those added to an object using the object literal notation or via an assignment:
var
literal
=
{
mine
:
"I pwn you"
};
literal
.
mine
;
// "I pwn you"
var
assigned
=
{};
assigned
.
mine
=
"I pwn you"
;
Own properties are also those added to this
and returned by constructor functions:
function
Builder
(
what
)
{
this
.
mine
=
what
;
}
var
constructed
=
new
Builder
(
"pwned"
);
constructed
.
mine
;
// "pwned"
Notice, however, that these two objects have access to a toString()
method that neither of them defined:
literal
.
toString
();
// "[object Object]"
constructed
.
toString
();
// "[object Object]"
The method toString()
is not an own method for either of the two objects. It’s something that came from a prototype
.
If you want to tell the difference between own properties and prototype
properties, you can use another method called hasOwnProperty()
, which takes a name of a property/method as a string:
literal
.
hasOwnProperty
(
'mine'
);
// true
constructed
.
hasOwnProperty
(
'mine'
);
// true
literal
.
hasOwnProperty
(
'toString'
);
// false
constructed
.
hasOwnProperty
(
'toString'
);
// false
literal
.
hasOwnProperty
(
'hasOwnProperty'
);
// false
Let’s do some more introspection. Where did that toString()
come from? How can you find out which is the prototype?
Objects have prototypes, but they don’t have a prototype
property—only functions do. However, many environments offer a special __proto__
property for each object. __proto__
is not available everywhere, so it’s useful only for debugging and learning.
__proto__
is a property that exposes the secret link between the object and the prototype
property of the constructor that created the object:
constructed
.
prototype
;
// undefined, objects don't have this property
constructed
.
constructor
===
Builder
;
// true, "who's your constructor?"
// Secret link - exposed!
constructed
.
constructor
.
prototype
===
constructed
.
__proto__
;
// true
Chaining __proto__
calls lets you get to the bottom of things. And the bottom is the built-in Object()
constructor function.
Object.prototype
is the mother of all objects. All objects inherit from it. That’s where toString()
was defined:
Object
.
prototype
.
hasOwnProperty
(
'toString'
);
// true
You can trace down toString
from the constructed
object:
constructed
.
__proto__
.
__proto__
.
hasOwnProperty
(
'toString'
);
// true
What about the literal
object? Its chain is one link shorter:
literal
.
__proto__
.
hasOwnProperty
(
'toString'
);
// true
This is because literal
wasn’t created by a custom constructor, which means it was created by Object()
behind the scenes, not by something (e.g., Builder()
) that inherits from Object()
.
When you need a simple object temporarily, you can use ({})
, and the following syntax also works:
({}).
__proto__
.
hasOwnProperty
(
'toString'
);
// true
Note
It was previously mentioned that object literals like var o = {}
create “empty” objects. The word “empty” is in quotes because the objects are not really empty or blank. Every object, even if it doesn’t have any own properties, has some properties and methods already available—the ones that come from its prototype chain.
When using constructor functions, you can add properties to this
or to the constructor’s prototype
. You may be wondering which one you should use.
Adding to the prototype
is more efficient and takes less memory because the properties and functions are created only once and reused by all objects created with the same constructor. Anything you add to this
will be created every time you instantiate a new object.
Therefore, any members you plan to reuse and share among instances should be added to the prototype
, and any properties that have different values in each instance should be own properties added to this
. Most commonly, methods go to the prototype and properties to this
, unless they are constant among the instances.
And talking about code reuse brings about the following question: Can you reuse code using inheritance?
- How to create objects with literal notation or with constructor functions
- What a prototype is (a property of each function)
- Own versus prototype properties
- Objects inherit properties from their prototypes and their prototypes’ prototypes, and so on
Now let’s talk a bit more about inheritance, because you’re probably wondering how do inheritance works in a language that doesn’t have classes. It turns out there’s more than one option to implement inheritance, depending on your goals and preferences.
The default way to implement inheritance is to use prototypes. You create one object using a parent constructor and set it as a prototype of the child constructor.
Here’s a constructor function that will be the parent:
function
NormalObject
()
{
this
.
name
=
'normal'
;
this
.
getName
=
function
()
{
return
this
.
name
;
};
}
And a second constructor:
function
PreciousObject
()
{
this
.
shiny
=
true
;
this
.
round
=
true
;
}
And the inheritance part:
PreciousObject
.
prototype
=
new
NormalObject
();
Voila! Now you can create precious objects with all the functionality of the normal objects:
var
crystal_ball
=
new
PreciousObject
();
crystal_ball
.
name
=
'Ball, Crystal Ball.'
;
crystal_ball
.
round
;
// true
crystal_ball
.
getName
();
// "Ball, Crystal Ball."
Note
Instead of the usual Car extends Vehicle
, the examples in this chapter are inspired by Jim Bumgardner’s blog post “Theory of the Precious Object”. It’s a fun read that suggests that treasured objects, such as J. R. R. Tolkien’s fictional One Ring or an iPhone, have certain common qualities.
Notice how you need to create an object with new NormalObject()
and assign it to the prototype
property of the PreciousObject
function because the prototype is just an object. If you think in terms of classes, you know that a class inherits from another. And if you carry that thought to JavaScript, you’d expect that a constructor inherits from a constructor. But that’s not the case. In JavaScript, you inherit an object.
If you have several constructor functions that inherit NormalObject
objects, you may create new NormalObject()
every time, but it’s not necessary. You can create one normal object and reuse it as a prototype of the children. Even the whole NormalObject
constructor may not be needed to begin with. Since you inherit an object, all you need is one object, regardless of how it’s created.
Another way to do the same is to create one (singleton) normal object using the object literal notation and use it as a base for the other objects:
var
normal
=
{
name
:
'normal'
,
getName
:
function
()
{
return
this
.
name
;
}
};
Then the objects created by PreciousObject()
can inherit normal
like this:
PreciousObject
.
prototype
=
normal
;
Since inheritance is all about reusing code, another way to implement it is to simply copy over properties from one object to another.
Imagine you have these objects:
var
shiny
=
{
shiny
:
true
,
round
:
true
};
var
normal
=
{
name
:
'name me'
,
getName
:
function
()
{
return
this
.
name
;
}
};
How can shiny
get the properties of normal
? Here’s a simple extend()
function that loops through and copies properties:
function
extend
(
parent
,
child
)
{
for
(
var
i
in
parent
)
{
if
(
parent
.
hasOwnProperty
(
i
))
{
child
[
i
]
=
parent
[
i
];
}
}
}
extend
(
normal
,
shiny
);
// Inherit
shiny
.
getName
();
// "name me"
Copying properties may look like overhead and something that can hurt performance, but for many tasks it’s just fine. You can also see that this is an easy way to implement mixins and multiple inheritance.
Note
With this pattern, instanceof
and isPrototypeOf()
do not work as expected.
Douglas Crockford, a JavaScript luminary and creator of JSON, popularized another way to implement inheritance by using a temporary constructor just to be able to set its prototype:
function
begetObject
(
o
)
{
function
F
()
{}
F
.
prototype
=
o
;
return
new
F
();
}
You create a new object, but instead of starting fresh, you inherit some functionality from another, already existing, object.
For example, say you have the following parent object:
var
normal
=
{
name
:
'name me'
,
getName
:
function
()
{
return
this
.
name
;
}
};
You can then have a new object that inherits from the parent:
var
shiny
=
begetObject
(
normal
);
The new object can be augmented with additional functionality:
shiny
.
round
=
true
;
shiny
.
preciousness
=
true
;
As you can see, there’s no property copying, nor any constructors in sight. A new object inherits from an existing one. This was actually embraced by the community as a good idea, and is now part of ECMAScript 5 in the form of Object.create()
, as you’ll see in Chapter 6.
As an exercise in closures and optimization, can you change begetObject()
so that F()
is not created every time?
Let’s wrap up with yet another way to implement inheritance, which is probably the closest to PHP because it looks like a constructor function inheriting from another constructor function; hence, it looks a bit like a class inheriting from a class.
Here’s the gist:
function
extend
(
Child
,
Parent
)
{
var
F
=
function
()
{};
F
.
prototype
=
Parent
.
prototype
;
Child
.
prototype
=
new
F
();
}
With this method, you pass two constructor functions to extend()
. After extend()
is done, any new objects created with the first constructor (the child) get all the properties and methods of the second (the parent) via the prototype
property.
Note
This method is often referred to as “classical” in quotes because it looks the closest to the idea of classes.
There are only two small things to add to extend()
:
- Have the child keep a reference to the parent, just in case.
-
Reset the
constructor
property of the child to point to the child’s constructor, in case this is needed for introspection (you’ll see some more about this property in Chapter 5):
function
extend
(
Child
,
Parent
)
{
var
F
=
function
()
{};
F
.
prototype
=
Parent
.
prototype
;
Child
.
prototype
=
new
F
();
Child
.
prototype
.
parent
=
Parent
;
Child
.
prototype
.
constructor
=
Child
;
}
In this method, there’s no instance of new Parent()
involved. This means that own properties added to this
inside the parent constructor will not be inherited. Only the properties added to the prototype of the parent will be inherited. And this is OK in many scenarios. In general, you add the properties you want to reuse to the prototype.
Consider this setup:
function
Parent
()
{
this
.
name
=
"Papa"
;
}
Parent
.
prototype
.
family
=
"Bear"
;
function
Child
()
{}
extend
(
Child
,
Parent
);
The property name
is not inherited, but the family
is:
new
Child
().
name
;
// undefined
new
Child
().
family
;
// "Bear"
And the child has access to the parent:
Child
.
prototype
.
parent
===
Parent
;
// true
Using call()
and apply()
gives you an opportunity to reuse code without having to deal with inheritance at all. After all, inheritance is meant to help us reuse code.
If you see a method you like, you can temporarily borrow it, passing your own object to be bound to this
:
var
object
=
{
name
:
"normal"
,
sayName
:
function
()
{
return
"My name is "
+
this
.
name
;
}
};
var
precious
=
{
shiny
:
true
,
name
:
"iPhone"
};
If precious
wants to benefit from object.sayName()
without extending anything, it can simply do the following:
object
.
sayName
.
call
(
precious
);
// "My name is iPhone"
If you combine method borrowing and classical inheritance, you can also get both own and prototype
properties:
function
Parent
(
name
)
{
this
.
name
=
name
;
}
Parent
.
prototype
.
family
=
"Bear"
;
function
Child
()
{
Child
.
prototype
.
parent
.
apply
(
this
,
arguments
);
}
extend
(
Child
,
Parent
);
All this
properties become own properties of the child. And via the magic of arguments
and apply()
, you can also have arguments passed to the constructors, if you so desire:
var
bear
=
new
Child
(
"Cub"
);
bear
.
name
;
// "Cub"
bear
.
family
;
// "Bear"
bear
.
hasOwnProperty
(
'name'
);
// true
bear
.
hasOwnProperty
(
'family'
);
// false
As you can see, there are many options available for implementing inheritance. You can pick and choose depending on the task at hand, personal preferences, or team preferences. You can even build your own solution or use one that comes with your library of choice. Note, however, that deep inheritance chains are not too common in JavaScript projects because the language allows you to simply copy properties and methods of other objects or “borrow” them to achieve your task. Or, as the Gang of Four’s Design Patterns (Addison-Wesley, 1994) says: “Prefer object composition to class inheritance.”
Get JavaScript for PHP Developers now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.