Using the prototype's methods and properties

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" 

Own properties versus prototype properties

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]" 

Overwriting a prototype's property with an own property

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 

Enumerating properties

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&section=products.

The following are a few details to be aware of:

  • Not all properties show up in a 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.
  • Prototypes that come through the prototype chain also show up, provided they are enumerable. You can check whether a property is an object's own property or a prototype's property using the hasOwnProperty() method.
  • The 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 

Using isPrototypeOf() method

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__.

The secret __proto__ link

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().

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.178.157