The idea that everything is an object in JavaScript is a bit of a misnomer. A more accurate saying would be that nearly everything is or can behave like an object. The reason for this is because nearly everything in JavaScript comes from a common ancestor called Object. This relationship is expressed through prototypes.
var str = "Hello";
var num = 42;
var obj = {};
In our example here, we have a string, a number and an empty object. The first two are what you would call primitives (a number primitive and a string primitive) while the last is a true object. Despite these differences, there are common methods defined on each.
var str = "Hello";
var num = 42;
var obj = {};
console.log(str.valueOf()); // Hello
console.log(num.valueOf()); // 42
console.log(obj.valueOf()); // Object {}
As you can see, each of our variables already has a method defined (valueOf()
).
This is because they all have a common ancestor: Object
. Think of it like
a really odd family tree where each child comes from a single parent. In the
family of JS, the root of this tree is Object
from which all the possible
children spawn.
The links between the parent and child form the prototype chain. Coming back
to our 3 variables above, we can check their prototype by calling
Object.getPrototypeOf()
.
var str = "Hello";
var num = 42;
var obj = {};
console.log(Object.getPrototypeOf(str)); // String
console.log(Object.getPrototypeOf(num)); // Number
console.log(Object.getPrototypeOf(obj)); // Object
This seems to make some sense. Our simple “str” variable has a prototype of
String
, “num” is Number
and “obj” is Object
. Let’s go deeper.
var str = "Hello";
var num = 42;
var obj = {};
console.log(Object.getPrototypeOf(str)); // String
console.log(Object.getPrototypeOf(num)); // Number
console.log(Object.getPrototypeOf(obj)); // Object
console.log( Object.getPrototypeOf( Object.getPrototypeOf(str))); // Object
console.log( Object.getPrototypeOf( Object.getPrototypeOf(num))); // Object
console.log( Object.getPrototypeOf( Object.getPrototypeOf(obj))); // null
What we learn here is that our string, number and object all have a common
ancestor in Object
. In addition, when we try and get the prototype of
Object
, we get back null
… the end of the line.
From this little experiment, we could draw the family tree for our string like this:
null -> [Object] -> [String] -> str
One of the great things about this is that we can define methods on a prototype and that method will be available to all the children that share that prototype. This is exactly why you can create a string and it will already have quite a few methods already defined on it.
var str = "i love lamp";
str.toUpperCase(); // "I LOVE LAMP"
Each type has its own set of methods. If you define an Array, it will have
an Array
prototype with predefined array methods like forEach, map
and
filter
. Also, because everything inherits from the Object
prototype,
any method defined on Object
is available to every other object
(like valueOf()
).
Please note, while it maybe tempting to start adding methods to Object’s prototype so you can use them everywhere, you should be very careful with this (or avoid it if at all possible). This causes problems if you do it in the wrong way, and can cause unexpected behavior even if you do it correctly. See this Stack Overflow post for more on this.
I will follow up in a future post on how to use prototypes and the new
operator to establish your own prototype chains.
Update: Classes in JavaScript