All the methods and properties you have added to the prototype
are available as soon as you create a new object using the constructor. If you create a newtoy
object using the Gadget()
constructor, you can access all the methods and properties that are already defined, as you can see in the following code:
> var newtoy = new Gadget('webcam', 'black'), > newtoy.name; "webcam" > newtoy.color; "black" > newtoy.whatAreYou(); "I am a black webcam" > newtoy.price; 100 > newtoy.rating; 3 > newtoy.getInfo(); "Rating: 3, price: 100"
It's important to note that the prototype
is live. Objects are passed by reference in JavaScript, and therefore, the prototype
is not copied with every new object instance. What does this mean in practice? It means that you can modify the prototype
at any time, and all the objects, even those created before the modification, will see the changes.
Let's continue the example by adding a new method to the prototype
:
Gadget.prototype.get = function (what) { return this[what]; };
Even though the newtoy
object was created before the get()
method was defined, the newtoy
object still has access to the new method, which is as follows:
> newtoy.get('price'), 100 > newtoy.get('color'), "black"
In the preceding example, getInfo()
was used internally to access the properties of the object. It could've also used Gadget.prototype
to achieve the same output, as follows:
Gadget.prototype.getInfo = function () { return 'Rating: ' + Gadget.prototype.rating + ', price: ' + Gadget.prototype.price; };
What's the difference? To answer this question, let's examine in detail how the prototype
works.
Let's take the newtoy
object again:
var newtoy = new Gadget('webcam', 'black'),
When you try to access a property of newtoy
, say, newtoy.name
, the JavaScript engine looks through all of the properties of the object searching for one called name
, and if it finds it, it returns its value, as follows:
> newtoy.name; "webcam"
What if you try to access the rating
property? The JavaScript engine examines all of the properties of the newtoy
object and doesn't find the one called rating
. Then, the script engine identifies the prototype
of the constructor function used to create this object (the same as if you do newtoy.constructor.prototype
). If the property is found in the prototype
object, the following property is used:
> newtoy.rating; 3
You can do the same and access the prototype
directly. Every object has a constructor
property, which is a reference to the function that created the object, so in this case look at the following code:
> newtoy.constructor === Gadget; true > newtoy.constructor.prototype.rating; 3
Now, let's take this lookup one step further. Every object has a constructor. The prototype
is an object, so it must have a constructor too, which, in turn, has a prototype
. You can go up the prototype chain, and you will eventually end up with the built-in Object()
object, which is the highest-level parent. In practice, this means that if you try newtoy.toString()
and newtoy
doesn't have its own toString()
method, and its prototype
doesn't either, in the end, you'll get the object's toString()
method:
> newtoy.toString(); "[object Object]"
As the preceding discussion demonstrates, if one of your objects doesn't have a certain property of its own, it can use one, if it exists, somewhere up the prototype chain. What if the object does have its own property and the prototype also has one with the same name? Then, the own property takes precedence over the prototype's.
Consider a scenario where a property name exists as both an own property and a property of the prototype
object:
> function Gadget(name) { this.name = name; } > Gadget.prototype.name = 'mirror';
Creating a new object and accessing its name
property gives you the object's own name
property, as follows:
> var toy = new Gadget('camera'), > toy.name; "camera"
You can tell where the property was defined using hasOwnProperty()
, which is as follows:
> toy.hasOwnProperty('name'), true
If you delete the toy
object's own name
property, the prototype's property with the same name shines through:
> delete toy.name; true > toy.name; "mirror" > toy.hasOwnProperty('name'), false
Of course, you can always recreate the object's own property as follows:
> toy.name = 'camera'; > toy.name; "camera"
You can play around with the hasOwnProperty()
method to find out the origins of a particular property you're curious about. The toString()
method was mentioned earlier. Where is it coming from?
> toy.toString(); "[object Object]" > toy.hasOwnProperty('toString'), false > toy.constructor.hasOwnProperty('toString'), false > toy.constructor.prototype.hasOwnProperty('toString'), false > Object.hasOwnProperty('toString'), false > Object.prototype.hasOwnProperty('toString'), true
If you want to list all the properties of an object, you can use a for...in
loop. In Chapter 2, Primitive Data Types, Arrays, Loops, and Conditions, you saw that you can also loop through all the elements of an array with for...in
, but as mentioned there, for
is better suited for arrays and for...in
for objects. Let's take an example of constructing a query string for a URL from an object:
var params = { productid: 666, section: 'products' }; var url = 'http://example.org/page.php?', i, query = []; for (i in params) { query.push(i + '=' + params[i]); } url += query.join('&'),
This produces the url
string as follows:
http://example.org/page.php?productid=666§ion=products
.
The following are a few details to be aware of:
for...in
loop. For example, the length (for arrays) and constructor properties don't show up. The properties that do show up are called enumerable. You can check which ones are enumerable with the help of the propertyIsEnumerable()
method that every object provides. In ES5, you can specify which properties are enumerable, while in ES3 you don't have that control.hasOwnProperty()
method.propertyIsEnumerable()
method returns false
for all of the prototype's properties, even those that are enumerable and show up in the for...in
loop.Let's see these methods in action. Take this simplified version of Gadget()
:
function Gadget(name, color) { this.name = name; this.color = color; this.getName = function () { return this.name; }; } Gadget.prototype.price = 100; Gadget.prototype.rating = 3;
Create a new object as follows:
var newtoy = new Gadget('webcam', 'black'),
Now, if you loop using a for...in
loop, you can see all of the object's properties, including those that come from the prototype:
for (var prop in newtoy) { console.log(prop + ' = ' + newtoy[prop]); }
The result also contains the object's methods, as methods are just properties that happen to be functions:
name = webcam color = black getName = function () { return this.name; } price = 100 rating = 3
If you want to distinguish between the object's own properties and the prototype's properties, use hasOwnProperty()
. Try the following first:
> newtoy.hasOwnProperty('name'), true > newtoy.hasOwnProperty('price'), false
Let's loop again, but this time, showing only the object's own properties:
for (var prop in newtoy) { if (newtoy.hasOwnProperty(prop)) { console.log(prop + '=' + newtoy[prop]); } }
The result is as follows:
name=webcam color=black getName = function () { return this.name; }
Now, let's try propertyIsEnumerable()
. This method returns true
for the object's own properties that are not built in, for example:
> newtoy.propertyIsEnumerable('name'), true
Most built-in properties and methods are not enumerable:
> newtoy.propertyIsEnumerable('constructor'), false
Any properties coming down the prototype chain are not enumerable:
> newtoy.propertyIsEnumerable('price'), false
However, not that such properties are enumerable if you reach the object contained in the prototype
and invoke its propertyIsEnumerable()
method. Consider the following code:
> newtoy.constructor.prototype.propertyIsEnumerable('price'), true
Objects also have the isPrototypeOf()
method. This method tells you whether that specific object is used as a prototype of another object.
Let's take a simple object named monkey
:
var monkey = { hair: true, feeds: 'bananas', breathes: 'air' };
Now, let's create a Human()
constructor function and set its prototype
property to point to monkey
:
function Human(name) { this.name = name; } Human.prototype = monkey;
Now, if you create a new Human
object called george
and ask If monkey
the prototype of george
?, you'll get true
:
> var george = new Human('George'), > monkey.isPrototypeOf(george); true
Note that you have to know, or suspect, who the prototype is and then ask is it true that your prototype is monkey
? in order to confirm your suspicion. But, what if you don't suspect anything, and you have no idea? Can you just ask the object to tell you its prototype? The answer is, you can't in all browsers, but you can in most of them. Most recent browsers have implemented the addition to ES5 called Object.getPrototypeOf()
.
> Object.getPrototypeOf(george).feeds; "bananas" > Object.getPrototypeOf(george) === monkey; true
For some of the pre-ES5 environments that don't have getPrototypeOf()
, you can use the special property, __proto__
.
As you already know, the prototype
property is consulted when you try to access a property that does not exist in the current object.
Consider another object called monkey
, and use it as a prototype when creating objects with the Human()
constructor:
> var monkey = { feeds: 'bananas', breathes: 'air' }; > function Human() {} > Human.prototype = monkey;
Now, let's create a developer
object, and give it the following properties:
> var developer = new Human(); > developer.feeds = 'pizza'; > developer.hacks = 'JavaScript';
Now, let's access these properties (for example, hacks
is a property of the developer
object):
> developer.hacks; "JavaScript"
The feeds
property can also be found in the object, as follows:
> developer.feeds; "pizza"
The breathes
property doesn't exist as a property of the developer
object, so the prototype is looked up, as if there is a secret link or passageway that leads to the prototype
object:
> developer.breathes; "air"
The secret link is exposed in most modern JavaScript environments as the __proto__
property, the word proto
with two underscores before and after:
> developer.__proto__ === monkey; true
You can use this secret property for learning purposes, but it's not a good idea to use it in your real scripts because it does not exist in all browsers (notably IE), so your scripts won't be portable.
Be aware that __proto__
is not the same as prototype
, as __proto__
is a property of the instances (objects), whereas prototype
is a property of the constructor functions used to create those objects:
> typeof developer.__proto__; "object" > typeof developer.prototype; "undefined" > typeof developer.constructor.prototype; "object"
Once again, you should use __proto__
only for learning or debugging purposes. Or, if you're lucky enough and your code only needs to work in ES5-compliant environments, you can use Object.getPrototypeOf()
.
3.145.178.157