Chapter 3. Objects

The specification document for ECMAScript defines the language as "an object-oriented programming language for performing computations and manipulating computational objects within a host environment." To put it simply, JavaScript is an object-oriented (OO) language.

The object-oriented approach focuses on objects, their composition, and how they interact with each other. While the whole area that is object-oriented programming is beyond the scope of this book, we'll discuss JavaScript's own flavor of object-oriented programming in this chapter.

Like the previous chapter on functions, there won't be much talk about MooTools here. The MooTools framework is an object-oriented framework at its core and we need to get a solid grasp of JavaScript's native object implementation to truly appreciate it. Don't worry, though, this is the last chapter where everything's vanilla JavaScript. We'll spend the rest of the book talking more about MooTools.

JavaScript is Prototypal(-ish)

While all OO languages deal with objects at their core, the process by which objects are created and composed divides most OO languages into two camps:

  • Classical (or class-based) object-oriented languages use classes to create objects. A class is a special data type that serves as a blueprint for creating objects. In a classical OO language, you define the structure of an object by creating a class for it and then create the object itself by creating an instance of the class, in a process called instantiation.

  • Prototypal (or prototype-based) object-oriented languages, on the other hand, do not have classes but rely on other objects as blueprints. In a prototypal language, you create an actual object called the prototype that reflects the structure you want and this object is then used as a blueprint for your other objects. You create new instances by copying the prototype itself in a process called cloning. In pure prototypal languages, all objects can be used as prototypes.

JavaScript, at its core, is a prototypal language: there are no classes in JavaScript and objects are created from other objects. However, JavaScript is not a purely prototypal language. In fact, it has traces of classical features that we'll see later on in this chapter. If you're already familiar with other object-oriented languages, you'll probably find JavaScript to be weird, since the language has a unique—and somewhat quirky—object implementation.

However, don't be turned off too quickly: JavaScript—as an object-oriented language—is quite flexible and its fusion of classical and prototypal features give it a highly powerful base for complex and rich applications.

A Language of Objects

A JavaScript object is essentially an aggregate of key-value pairs. It's a complex data type, in contrast to "simple" data types like strings and numbers. Each key-value pair in an object is called a property, the key being the property name and the value being the property value.

The property name is always a string, but the property value can be any data type: primitives like strings, numbers, or Booleans, or complex data types like arrays, functions, and other objects. Although JavaScript makes no distinction about what data type a property contains, properties with functions as their values are often called methods to distinguish them from other properties. To avoid confusion, we'll adopt this practice in our discussion: properties with values other than functions will be called "properties" and properties with functions as their values will be called "methods". When we need to refer to both the properties and methods of an object, we'll use another term from object-oriented programming: members.

Note

The lack of distinction between properties and methods in JavaScript arises from the fact that the language has first-class functions. From the perspective of the interpreter, any member of an object—regardless of value—is a property, since functions themselves can be used as values.

There are no limits to the number of properties an object can have, and an object can also have zero properties (making it an empty object). Depending on its use, an object can sometimes be called a hash or a dictionary or a table, reflecting its structure as a set of key-value pairs. However, we'll stick to object in our discussion.

The easiest way to create a new object in JavaScript is using the object literal:

// an object literal
var person = {
    name: 'Mark',
    age: 23
};

Here, we create a new object with two properties, one with the key name and the other with the key age, and store it in our person variable—giving us a person object with two members. Notice that we didn't wrap the keys in quotes even though keys have to be strings. This is allowed in JavaScript as long as the keys are valid identifiers and not reserved words. Otherwise, we have to wrap our keys in quotes:

// object literal with different keys
var person = {
    'name of the person': 'Mark',
    'age of the person': 23
};

To access a member of an object, we can use dot notation, which involves appending a period to the object's identifier and then the name of the key we want to access, or bracket notation, which involves appending a pair of square brackets, [], to the end of the identifier containing a string value corresponding to the key:

var person = {
    name: 'Mark',
    age: 23
};
// dot notation
console.log(person.name); // 'Mark'

// bracket notation
console.log(person['age']); // 23

The dot notation is actually just a shortcut—or syntactic sugar—for the bracket notation, although it's common practice to use dot notation most of the time. Of course, dot notation is limited to keys that are proper identifiers; for everything else, you'll have to use bracket notation.

var person = {
    'name of the person': 'Mark',
    'age of the person': 23
};

console.log(person['name of the person']); // 'Mark'

You'll also use bracket notation when you're not using a string literal for the key, but a variable that holds a string literal:

var person = {
    name: 'Mark',
    age: 23
};

// variable to hold the key
var key = 'name';

console.log(person[key]); // 'Mark'

Accessing an object's member that's not set will return the value undefined:

var person = {};

console.log(person.name); // undefined

You can set the value of an object's member by explicitly defining it during the creation of the object like we did in the previous examples, but you can also set or modify a member's value by simply assigning a new value:

var person = {name: 'Mark'};

person.name = 'Joseph';
console.log(person.name); // 'Joseph'

console.log(person.age); // undefined
person.age = 23;
console.log(person.age); // 23

You can create methods by simply assigning a function value to an object's member:

var person = {
    name: 'Mark',
    age: 23,
sayName: function(){
        console.log(this.name); // 'Mark'
    }
};

console.log(typeof person.sayName); // 'function'
person.sayName();

person.sayAge = function(){
    console.log(this.age); // 23
};

console.log(typeof person.sayAge); // 'function'
person.sayAge();

You'll notice that we referred to the name and age members of our person object using this.name and this.age in our methods. If you recall our discussion in the previous chapter, you know that the this keyword for functions that are properties of objects refers to the object itself. In our case, the sayName and sayAge functions are methods of the person object, and therefore the value of the this keyword in their function bodies points to the person object.

The Building Blocks of Objects

While object literals are an easy way to create new objects, they don't showcase JavaScript's full object-oriented capabilities. For one, limiting yourself to object literals would be time-consuming: if you need 30 objects to represent people with the properties name and age and logName and logAge methods, it would be impractical to create literals for each of these objects. In order to be efficient, we need a way to define the structure of our objects and use this definition to create new instances of the objects.

In a classical object-oriented language, we can create a new Person class to define the structure of our objects. In a prototypal object-oriented language, we can simply create a base Person object for the structure, then clone this object to create new instances. JavaScript—while nominally a prototypal language—takes an approach that fuses both the classical and prototypal way.

Constructor Functions

The first half of JavaScript's approach involves constructor functions (or simply, constructors). Object literals are actually syntactic sugar for creating objects without having to use a constructor. The following objects are equivalent:

// using an object literal..
var personA = {
    name: 'Mark',
    age: 23
};

// using a constructor..
var personB = new Object();
personB.name = 'Mark';
personB.age = 23;

The function Object is our constructor function, and using var personB = new Object() is the same as using var personB = {}. By using new Object(), we create a new blank object and this new object is said to be an instance of Object.

The Object constructor is special because it represents the "base" object in JavaScript: all objects in JavaScript, regardless of which constructor was used to create them, are instances of Object. You can check whether an object is an instance of a constructor using the instanceof operator:

// object literal
var personA = {};

// constructor
var personB = new Object();

// check whether objects are instances of Object
console.log(personA instanceof Object); // true
console.log(personB instanceof Object); // true

All objects also have a special property called constructor, which is a reference to the constructor function that was used to create it. In the case of our simple objects above, the value of this property will be the Object constructor:

// object literal
var personA = {};

// constructor
var personB = new Object();

// check whether objects used Object constructor
console.log(personA.constructor == Object); // true
console.log(personB.constructor == Object); // true

As the name implies, constructor functions are, obviously, functions. In fact, any JavaScript function (with the exception of host or interpreter-implemented functions) can be used as a constructor. This is one of the unique aspects of the language's object implementation: instead of creating a new construct for instantiating objects, JavaScript just relies on the readily available function construct.

Of course, you won't be using every function you create as a constructor. In almost all cases, you'll create your own functions that serve the sole purpose of being constructors for your classes. A constructor function is just like any other function—with slight changes in its internals—and it's common practice to define functions with their names capitalized to denote their nature as constructors:

// our Person constructor
var Person = function(){};

// using the Person function as a regular function
var result = Person();
console.log(result); // undefined

// using the Person function as a constructor
var person = new Person();

console.log(typeof person); // 'object'
console.log(person instanceof Person); // true
console.log(person.constructor == Person); // true

We created a new constructor called Person by simply defining a blank function. When the Person function is called like a normal function, it returns undefined. However, when we use the new keyword in conjunction with the Person() invocation, something different happens: it returns a new object. It is this combination of the new keyword and a call to a constructor function that makes object instantiation happen.

In our example, new Person() returns a blank object, just like the object returned by new Object(). The only difference in this case is that the object returned is no longer just an instance of Object, it's also be an instance of Person, and the constructor property of the object now points to our new Person constructor rather than Object. But the object returned is still a blank object.

If you recall from the last chapter, the this keyword inside functions refers to an object. In the case of our Person function, it should refer to the global object when the function is used as a regular function (because it was declared in the global scope). However, something changes when the Person function is used as a constructor: this no longer points to the global object, but instead points to the new object created:

// a global object
var fruit = 'banana';

// our constructor
var Person = function(){
    console.log(this.fruit);
};

// Person as a regular function
Person(); // logs 'banana'

// Person as a constructor
new Person(); // logs undefined

We get undefined for the last line because this.fruit no longer points to any identifier available. It's the job of the new keyword to create a new object and change the this value of the constructor function to point to the new object.

At the start of this section, we ran into a problem with using simple object literals for object creation. We needed a way to create several copies of the person object without having to type each one of them via literals. Now that we know that constructor functions can be used to create objects and that the this keyword refers to the new object, we can use this to solve our problem:

var Person = function(name, age){
    this.name = name;
    this.age = age;
};

var mark = new Person('Mark', 23);
var joseph = new Person('Joseph', 22);
var andrew = new Person('Andrew', 21);

console.log(mark.name); // 'Mark'
console.log(joseph.age); // 22
console.log(andrew.name + ', ' + andrew.age); // 'Andrew, 21'

The first thing you'll notice is that we changed our constructor a bit to accept arguments. This is allowed because constructors are just like regular functions, with the exception of having their this keyword pointing to the newly created instance. When the call to new Person is interpreted, a new object is created and the Person function is invoked. Inside the constructor function, the arguments to name and age are set as the values of the object properties of the same name. The object is then returned.

Using constructors makes instantiating new objects of the same structure easier. Instead of explicitly defining the structure of each object via literals, you can simply create a constructor function that defines the structure ahead of time. This comes in handy when you need to add more members to your objects, especially when it comes to methods:

var Person = function(name, age){
    this.name = name;
    this.age = age;
    this.log = function(){
        console.log(this.name + ', ' + this.age);
    };
};

var mark = new Person('Mark', 23);
var joseph = new Person('Joseph', 22);
var andrew = new Person('Andrew', 21);

mark.log(); // 'Mark, 23'
joseph.log(); // 'Joseph, 22'
andrew.log(); // 'Andrew, 21'

Here we added a new method called log to our objects by simply declaring the value of this.log as a logging function inside our constructor. If we were still using object literals, we would have to define the log function for each of our objects—and that would take too long. But by using a constructor function, we can define the function once for all our objects.

Prototypes

While constructor functions might seem like the end point for JavaScript's object implementation, they're only half of the picture. And if we limit ourselves to using constructor functions, we'll run into several problems.

One such problem is code organization. At the start of the last section, we wanted an easier way to create person objects with the properties name and age, and the methods setName, getName, setAge, and getAge. If we modify the Person constructor from the previous section according to our specification, we end up with something like this:

var Person = function(name, age){

    // properties
    this.name = name;
    this.age = age;

    // methods
    this.setName = function(name){
        this.name = name;
    };

    this.getName = function(){
        return this.name;
    };

    this.setAge = function(age){
        this.age = age;
    };
this.getAge = function(){
        return this.age;
    };

};

Our Person constructor has suddenly ballooned—and we only have two properties and four methods! Imagine if you start creating complex objects with lots of members and complex methods. Your code would soon get hard to manage if you put everything inside your constructor.

Another issue that arises is extensibility. Suppose we have the following code:

// constructor.js
var Person = function(name, age){
    this.name = name;
    this.age = age;
    this.log = function(){
        console.log(this.name + ', ' + this.age);
    };
};

// program.js
var mark = new Person('Mark', 23);
mark.log(); // 'Mark, 23'

In this example, the Person constructor comes from another file we load from an external source into our program and we don't have access to change the code. But for our program, we need to add the new methods getName and getAge to our Person instances. Since we can't modify the constructor itself, we can't add these new methods inside the constructor.

Then we get a brilliant idea: let's add the methods to the instances! Since new members can easily be added to methods by assignment, this would be easy to do. However, we quickly run into some problems:

// program.js
var mark = new Person('Mark', 23);
mark.log(); // 'Mark, 23'

mark.getName = function(){ return this.name; };
mark.getAge = function(){ return this.age; };

mark.getName(); // returns 'Mark'
mark.getAge(); // returns 23

var joseph = new Person('Joseph', 22);
joseph.log(); // 'Joseph, 22'

// the following lines will produce an error:
joseph.getName();
joseph.getAge();

Even though we successfully managed to add new methods to our mark instance, our joseph instance didn't get the same methods. We end up with the same problem we had with object literals: we have to define the same set of members for each of our objects. This isn't very practical, even if we build a helper function to do it.

At the beginning of this chapter, we learned that JavaScript is a prototypal language and that the main feature of a prototypal language is its reliance on creating copies of an original object—the prototype—to define new objects instead of using classes. But looking back, we haven't seen any copying involved, nor did we see any original objects that serve as prototypes. All we saw were functions used as constructors and that new keyword.

And that's our clue: the new keyword. Remember that when we use new Object(), the new keyword creates a new object and uses that object as the this value of our constructor function. Actually, the new keyword isn't creating a new object: it's copying an object. And the object it's copying is none other than the prototype.

All functions that can be used as constructors have a special property called the prototype, which is an object that defines the structure of your instances. When you use new Object(), a new copy of Object.prototype is made and this becomes your new object instance. This is another unique trait of JavaScript: unlike other prototypal languages where any object can be a prototype, JavaScript defines special prototype objects for the sole purpose of prototyping.

Note

There is a way to mimic the prototypal style of other languages in JavaScript, though, wherein you directly clone any object to create a new object, instead of relying on prototypes. We'll learn how to do this in the last section of this chapter.

The prototype object, like any other object, can have an unlimited number of members, and adding new members to it is simply a matter of assigning new values. We could rewrite our original Person code in this way:

var Person = function(name, age){
    this.name = name;
    this.age = age;
};

Person.prototype.log = function(){
    console.log(this.name + ', ' + this.age);
};

var mark = new Person('Mark', 23);
mark.log(); // 'Mark, 23'

Here, we moved the declaration of our log method outside of the constructor. By assigning Person.prototype.log, we tell the interpreter that all objects created from the Person constructor should have a log method, and this is reflected in our last line where we call mark.log(). The rest of the constructor stays the same: we didn't move our this.name and this.age properties to the prototype because we want to be able to set them when we invoke our constructor.

With prototypes in mind, we could also rewrite the code we had at the start of this section into something more manageable:

var Person = function(name, age){
    this.name = name;
    this.age = age;
};

Person.prototype.setName = function(name){
this.name = name;
};

Person.prototype.getName = function(){
    return this.name;
};

Person.prototype.setAge = function(age){
    this.age = age;
};

Person.prototype.getAge = function(){
    return this.age;
};

This new code is much cleaner because we're not cramming everything inside our constructor and we can easily add more methods in the future without having to rearrange the constructor.

Another problem we had was how to add new methods when we can't change the constructor function. But since we already have access to the constructor function (and therefore its prototype), we can easily add new members without any access to the constructor itself:

// person.js
var Person = function(name, age){
    this.name = name;
    this.age = age;
};

// program.js
Person.prototype.log = function(){
    console.log(this.name + ', ' + this.age);
};

var mark = new Person('Mark', 23);
mark.log(); // 'Mark, 23'

var joseph = new Person('Joseph', 22);
joseph.log(); // 'Joseph, 22'

We actually already saw a sample of dynamic prototype augmentation in the previous chapter on functions. One function form, the function object, used the Function constructor and we were able to add new function methods through the MooTools function called Function.implement. All JavaScript functions are actually instances of Function, and Function.implement actually modifies Function.prototype. Even though we didn't have access to the Function constructor itself—which is a built-in constructor that's provided by the interpreter—we're still able to add new function methods by augmenting Function.prototype. The augmentation of native types will be discussed later on in the chapter on Types and Natives.

Inheritance

To understand prototypal programming in JavaScript, we need to be able to distinguish between prototypes and instances. As we learned earlier, a prototype is an object we use like a blueprint to define the structure of objects we want. When we copy the prototype, we create an instance of the prototype:

// Animal constructor
var Animal = function(name){
    this.name = name;
};

// Animal prototype
Animal.prototype.walk = function(){
    console.log(this.name + ' is walking.'),
};

// Animal instance
var cat = new Animal('Cat'),
cat.walk(); // 'Cat is walking.'

In this example, Animal and its prototype define the structure of our Animal objects, and our object cat is an instance of Animal. When we execute new Animal(), a copy of Animal.prototype is created and we call this copy as an instance. Animal.prototype is an object with a single member, walk, which is a method and therefore all instances of Animal also have the same walk method.

But what happens if we change Animal.prototype after we created an instance?

// Animal constructor
var Animal = function(name){
    this.name = name;
};

// Animal prototype
Animal.prototype.walk = function(){
    console.log(this.name + ' is walking.'),
};

// Animal instance
var cat = new Animal('Cat'),
cat.walk(); // 'Cat is walking.'

// Does an Animal have an eat method?
console.log(typeof cat.eat); // undefined, so no.

// Add an eat method to Animal
Animal.prototype.eat = function(){
    console.log(this.name + ' is eating.'),
};

console.log(typeof cat.eat); // 'function'
cat.eat(); // 'Cat is eating'

Something interesting happened here. When we first checked the value of cat.eat after we created the cat object, we found that cat.eat was undefined. We then added a new method to Animal.prototype called eat and checked again: cat.eat is no longer undefined but is now a function. In fact, it's the same function we defined for Animal.prototype.

It seems that defining new properties for a prototype updates all instances of the prototype, regardless of when they were created. Remember that when we create a new object, the new operator creates a new copy of the prototype and when we created cat, the prototype only had one method. If it's a real copy, it shouldn't have the eat method after we defined it in the prototype. After all, if you take a document and copy it on a photocopy machine and then write something on the original using a pen, you wouldn't expect that new change to automatically appear in the photocopied version, right?

Perhaps the interpreter knows when new properties are added to the prototype and automatically adds them to the instances? Maybe after we add the new eat method to Animal.prototype, it searches for all instances of Animal and then adds this new method to them? We can easily check if this is the case by doing a simple experiment. After creating a cat instance, we'll give it its own eat method, and then we'll update the prototype. If the interpreter indeed copies methods from the prototype, the eat method of our cat instance should be overwritten:

// Animal constructor
var Animal = function(name){
    this.name = name;
};

// Animal prototype
Animal.prototype.walk = function(){
    console.log(this.name + ' is walking.'),
};

// Animal instance
var cat = new Animal('Cat'),
cat.walk(); // 'Cat is walking.'

// Add a new eat method to cat
cat.eat = function(){
    console.log('Meow. Cat is eating!'),
};

// Add an eat method to Animal
Animal.prototype.eat = function(){
    console.log(this.name + 'is eating.'),
};

cat.eat(); // 'Meow. Cat is eating!'

Clearly, that's not the case. The JavaScript interpreter does not update the instances, because our cat.eat method points to the one we defined for the cat object rather than the one from Animal.prototype. So what's really happening?

All objects have an internal property called proto that points to the object's prototype. This property is used by the interpreter to "link" the object to its prototype. While it's true that the new keyword creates a copy of the prototype, it actually just creates a "superficial" copy, in the sense that the object it creates looks like the prototype. But the truth is that the object created by new is nothing more than a blank object that has its internal proto property set to the prototype of the constructor.

You're probably asking, "Wait, if it's a blank object, why does it have methods and properties like the prototype? Where do those come from?" This is where the proto property comes in. Objects are linked to their prototype so that the methods and the properties of the prototype can be accessed from the object. In our example, our cat object didn't really have its own walk method—it was actually the walk method of Animal.prototype. When the interpreter comes across the identifier cat.walk(), it checks whether the cat object has its own walk method. Since we didn't explicitly define a walk method for our cat object, it checks the proto property of the object to find its prototype, then checks the prototype to see if it has a walk method. And seeing that there is indeed such a method in the prototype, the interpreter uses this method for the instance.

This also explains the eat method of our other example: because we explicitly defined an eat method for our cat object, the eat method of our Animal.prototype wasn't called. An object overrides the members of the prototype if it has a member of the same key, and the members of an object's prototype are available only when they aren't overridden by the object's own members.

An object's member that's from the prototype (as opposed to a member that was explicitly defined for it) is said to be inherited, and the process of obtaining members from an object's prototype is called inheritance. You can check if an object has its own member using the hasOwnProperty method that's available to all objects:

var Animal = function(){};
Animal.prototype.walk = function(){};

var dog = new Animal();

var cat = new Animal();
cat.walk = function(){};

console.log(cat.hasOwnProperty('walk')); // true
console.log(dog.hasOwnProperty('walk')); // false

Here we explicitly defined a walk method for our cat object, but we didn't do the same for our dog object. When we call cat.hasOwnProperty('walk'), it returns true because cat now has its own walk member. In contrast, dog.hasOwnProperty('walk') returns false because there's no explicitly defined walk member for dog. Interestingly, if we do cat.hasOwnProperty('hasOwnProperty'), it'll return false, because the hasOwnProperty method itself is inherited from Object.

One thing to consider is the value of this: inside a constructor, the value of this always points to the instance and never to the prototype. Functions defined from the prototype have another rule: if they are called directly from the prototype, the this value will point to the prototype object, but when they are called from an object inheriting from the prototype, this will point to the instance rather than the prototype:

var Animal = function(name){
    this.name = name;
};

Animal.prototype.name = 'Animal';

Animal.prototype.getName = function(){
    return this.name;
};

// Call `getName` directly from the prototype
Animal.prototype.getName(); // returns 'Animal'

var cat = new Animal('Cat'),
cat.getName(); // returns 'Cat'

Here we changed our code a bit so that the Animal.prototype object will have its own name property. When we called the function directly, this.name points to the value of the name property of Animal.prototype. However, when we called cat.getName(), this.name points to the value of cat.name (which we set in our constructor).

The prototype and its instances are separate objects, and the link between them goes only one way: changes to the prototype are reflected to all its instances but changes to the instances affect only the instances. However, weird behavior occurs when complex types come into play wherein changes to one instance affect all other instances.

Remember that in JavaScript we have both primitives and complex types. Primitives such as strings, numbers, and Booleans are always used by value: a copy is made when they are passed to functions or assigned to variables. However, complex types like arrays, functions, and objects are always used by reference, which means that they aren't copied but rather a "pointer" to the original complex type is created:

// Create an object
var object = {name: 'Mark'};

// "Copy" the object into another variable
var copy = object;

console.log(object.name); // 'Mark'
console.log(copy.name); // 'Mark'

// Change the value of our copy
copy.name = 'Joseph';

console.log(copy.name); // 'Joseph'
console.log(object.name); // 'Joseph'

When we did var copy = object, no new object was created. Rather, our variable copy was assigned a reference to the same original object that was the value of object. Therefore, both copy and object now reference the same object. A change made to copy was therefore reflected to object because they're really pointing to the same object.

Objects can have members that are complex types, such as an object with a property that's another object. Prototypes, being objects themselves, can also have complex types as properties. The problem arises when you define a property in a prototype that points to a complex object, because this property will then be inherited by all instances and they will all therefore point to a single object:

var Animal = function(){};

Animal.prototype.data = {name: 'animal', type: 'unknown'};

Animal.prototype.setData = function(name, type){
    this.data.name = name;
    this.data.type = type;
};

Animal.prototype.getData = function(){
    console.log(this.data.name + ': ' + this.data.type);
};

var cat = new Animal();
cat.setData('Cat', 'Mammal'),
cat.getData(); // 'Cat: Mammal'

var shark = new Animal();
shark.setData('Shark', 'Fish'),
shark.getData(); // 'Shark: Fish'

cat.getData(); // 'Shark: Fish'

Both the cat and shark objects don't have their own data property, so they inherit the object from Animal.prototype. Because of this inheritance, cat.data and shark.data both point to the same object that was defined from Animal.prototype. Any change in one instance therefore gets reflected in other instances and gives us unwanted behavior.

The easiest way to solve this would be to remove the data property from Animal.prototype and give our instances their own data property. This could easily be done inside the constructor:

var Animal = function(){
    this.data = {name: 'animal', type: 'unknown'};
};

Animal.prototype.setData = function(name, type){
    this.data.name = name;
    this.data.type = type;
};

Animal.prototype.getData = function(){
    console.log(this.data.name + ': ' + this.data.type);
};

var cat = new Animal();
cat.setData('Cat', 'Mammal'),
cat.getData(); // 'Cat: Mammal'

var shark = new Animal();
shark.setData('Shark', 'Fish'),
shark.getData(); // 'Shark: Fish'

cat.getData(); // 'Cat: Mammal'

Because the this keyword inside our constructor points to the instance itself, setting this.data inside it would give the instance its own data property and won't affect our prototype. The end result is that all our instances now have their own data properties, and changing these properties will no longer affect other instances.

The Prototype Chain

The Object constructor and its corresponding prototype is the base object in JavaScript. All objects, regardless of how they are constructed, inherit from Object. In the case of the following code, this is simple enough to understand:

var object = new Object();

console.log(object instanceof Object); // true

Because we constructed object using the Object constructor, we can say that the internal proto property of object points to Object.prototype. However, consider the following code:

var Animal = function(){};

var cat = new Animal();

console.log(cat instanceof Animal); // true
console.log(cat instanceof Object); // true
console.log(typeof cat.hasOwnProperty); // 'function'

We know for a fact that cat is indeed an instance of Animal, since it was created using new Animal(). We also know that all objects have a method called hasOwnProperty, which they inherit from Object. But how can our cat object inherit from Object when its internal proto property points to Animal? And how can cat be an instance of both Animal and Object at the same time when we didn't even use the Object constructor?

The answer lies within the prototypes. By default, a constructor's prototype is a basic object that has no methods or properties of its own. Sound familiar? Yes, it's like an object that's created using new Object()! We could have written our code in the following form:

var Animal = function(){};

Animal.prototype = new Object();

var cat = new Animal();

console.log(cat instanceof Animal); // true
console.log(cat instanceof Object); // true
console.log(typeof cat.hasOwnProperty); // 'function'

And now it's clearer that Animal.prototype inherits from Object. Aside from inheriting from its own prototype, an instance also inherits from the prototype of the prototype of the prototype.

Sound confusing? Let's examine this in our code above. Our cat object is created using new Animal, so it inherits properties and methods from Animal.prototype. The value of Animal.prototype is an object created using new Object(), so it inherits the properties and methods from Object.prototype. In turn, these properties and methods that our Animal.prototype inherits from Object.prototype are also passed to any instances of Animal. And, therefore, we can say that our cat object indirectly inherits from Object.prototype.

The internal proto property of our cat object points to Animal.prototype, and, in turn, the internal proto property of our Animal.prototype points to Object.prototype. This continuous linking between the prototypes is called the prototype chain, and we can say that the cat object's prototype chain extends from the cat object itself to Object.prototype.

Note

Object.prototype is always the end of the prototype chain, and this prototype's proto property does not point to any other object—otherwise there would be no end to the prototype chain and it would be impossible to traverse it. Object.prototype itself is not created using any constructors, but rather set by the interpreter internally, thus making it the only object that is not an instance of Object.

The process of looking up properties and methods of an object through the prototype chain is called traversal. When the interpreter encounters cat.hasOwnProperty, it first looks at the object itself to see if there's a member called hasOwnProperty in the object. When it doesn't find one, it looks at the next object in the prototype chain, which is Animal.prototype. As there's still no such member in this object, it moves on to the next object in the prototype chain, and so on. If it finds the member we're looking for in one of the objects in the chain, it stops the traversal and uses this member. If it reaches the end of the chain (which is Object.prototype) and still does not find what it's looking for, it returns the value undefined for the member. In our example, the traversal ends at Object.prototype, where the hasOwnProperty method comes from.

An object is always an instance of at least one constructor: for objects created using literals and objects created using new Object(), they're instances of Object. For objects created using a different constructor, they will be an instance of both the constructor used to create them as well as an instance of all the constructors used to create the prototypes in their prototype chain.

Deliberate Chains

The prototype chain becomes useful once we start creating more complex objects. Say we want to have an Animal object: all animals have a name, and all animals should be able to eat to survive. Therefore, we could write the following code:

var Animal = function(name){
    this.name = name;
};

Animal.prototype.eat = function(){
    console.log('The ' + this.name + ' is eating.'),
};

var cat = new Animal('cat'),
cat.eat(); // 'The cat is eating.'

var bird = new Animal('bird'),
bird.eat(); // 'The bird is eating.'

So far so good. But now we want our animals to make noises, so we need to add methods. Moreover, these animals make different sounds: a cat meows and a bird tweets, so a cat needs a meow method and a bird needs a tweet method. Of course, we can simply create these methods on the animals themselves, but it would be impractical since we plan on creating many cat and bird objects. We could also simply add both these methods to Animal.prototype, but that would be wasteful since birds never meow and cats never tweet.

What if we simply create separate constructors for both objects? We can create Cat and Bird constructors and modify their prototypes to fit the particular animal. But both these animals eat—do we really need to define an eat method for each of the animals? That would mean quite a number of repeated declarations should we decide to add more types of animals.

With our knowledge of the prototype chain, we realize that there's a better solution. We can code our program so that we have separate Cat and Bird objects with proper methods and still inherit the methods that both animal share from our original Animal prototype:

var Animal = function(name){
    this.name = name;
};

Animal.prototype.eat = function(){
    console.log('The ' + this.name + ' is eating.'),
};

var Cat = function(){};

Cat.prototype = new Animal('cat'),

Cat.prototype.meow = function(){
    console.log('Meow!'),
};

var Bird = function(){};

Bird.prototype = new Animal('bird'),
Bird.prototype.tweet = function(){
    console.log('Tweet!'),
};

var cat = new Cat();
cat.eat(); // 'The cat is eating.'
cat.meow(); // 'Meow!'

var bird = new Bird();
bird.eat(); // 'The bird is eating.'
bird.tweet(); // 'Tweet!'

We left our original Animal constructor and prototype as they were and created two new constructors, Cat and Bird. We used empty functions for our new constructors since we didn't have anything to set inside them, but we could have as easily added some other statements inside them if needed. The default prototypes for Cat and Bird were replaced with instances of Animal, so our Cat and Bird objects will also inherit from Animal.prototype. Finally, we added proper methods to the prototypes—meow for Cat and tweet for Bird. When we finally instantiated our objects, the results were two objects that inherit from both their immediate prototypes and from Animal.prototype.

In classical programming languages, the process of creating a specialized version of a class by creating a new class and inheriting directly from the original class is called subclassing. JavaScript, being a prototypal language, does not have classes and, in essence, the only thing we are doing is creating a deliberate prototype chain. We say "deliberate" because we explicitly set which objects will be included in the prototype chain of our instances.

There is no limit to the size of your prototype chain, and you can extend your chain to allow for more specialization:

var Animal = function(name){
    this.name = name;
};

Animal.prototype.eat = function(){
    console.log('The ' + this.name + 'is eating.'),
};

var Cat = function(){};

Cat.prototype = new Animal('cat'),

Cat.prototype.meow = function(){
    console.log('Meow!'),
};

var Persian = function(){
    this.name = 'persian cat';
};

Persian.prototype = new Cat();

Persian.prototype.meow = function(){
    console.log('Meow...'),
};

Persian.prototype.setColor = function(color){
this.color = color;
};

Persian.prototype.getColor = function(){
    return this.color;
};

var king = new Persian();
king.setColor('black'),
king.getColor(); // 'black'
king.eat(); // 'The persian cat is eating.'
king.meow(); // 'Meow...'

console.log(king instanceof Animal); // true
console.log(king instanceof Cat); // true
console.log(king instanceof Persian); // true

Here we created a new specialized version of Cat called Persian. You'll notice that we created a Persian.prototype.meow method, which overwrites (during traversal) the original Cat.prototype.meow method for instances of Persian. If you check, you'll see that the king object is an instance of Animal, Cat, and Persian, which means our prototype chain was correctly set.

The real power of prototype chains (deliberate or not) is when we see it in conjunction with inheritance and prototype chain traversal. Because all of the prototypes are linked, a change at one point in the chain will be reflected in the items below that point. If we add a new method or property to Animal.prototype, for instance, all prototypes that inherit from Animal will also receive those new members. This gives us a way to extend several objects easily and quickly.

As your programs grow in complexity, deliberate chains help keep your code organized. Instead of jamming all your code into one prototype, you can create multiple prototypes that have deliberate chains to reduce the amount of code you're working with and keep your program manageable.

Simplified Prototypal Programming

You should realize by now that JavaScript's object-oriented flavor is in a class of its own. JavaScript's status as a prototypal language is largely nominal: constructor functions and the new keyword are elements you'd expect to find in classical languages, and JavaScript's use of inheritance from prototypes—while clearly a prototypal characteristic—relies on specialized prototype objects, making them similar to classes. The design of the language's object implementation was in part affected by language politics: JavaScript was created during a time when classical languages were the standard, and it was decided to give the language some features that would be familiar to classical programmers.

However, JavaScript is a flexible language. While we may not be able to change the core implementation of objects, we can leverage what's already available to give the language a more pure prototypal feel (and as we'll see in the next chapter, even a more classical feel).

In this "simplified" prototypal model, we'll forgo the complexities of JavaScript prototyping and focus on objects themselves. Instead of creating a constructor and setting prototypes, we'll use real objects as the prototypes and create new objects by "cloning" these prototypes. To get a better idea of what we're going to do, let's first use an example from another prototypal programming language called Io:

Animal := Object clone
Animal name := "animal"

Cat := Animal clone
Cat name := "cat"

myCat := Cat clone

Since this isn't a book about Io, we'll stick to the basics. Like JavaScript, the base object of Io is called Object. However, Io's Object isn't a constructor (i.e., not a function), but rather a real object. At the start of our code, we created a new object, Animal, by cloning the original Object object. Object clone in Io means "access the clone method of Object and execute it," since Io uses spaces instead of periods or brackets to access properties. Next we set the name property of Animal to the proper string and then created a new object called Cat by cloning the Animal object and also set a name property for it. Finally, we created our myCat object by cloning our final Cat object.

We can do something similar in JavaScript:

var Animal = function(){};
Animal.prototype = new Object();
Animal.prototype.name = 'animal';

var Cat = function(){};
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

var myCat = new Cat();

Similar, but not exactly the same. In our Io example, the final myCat object inherits directly from Cat, Animal, and Object, which are all actual objects and not constructors. In our JavaScript example, our final myCat object inherits from Cat, Animal, and Object via their prototype properties, and Cat, Animal, and Object are functions rather than objects. In other words, Io does not have constructors and it clones directly from objects, while JavaScript has constructor functions and clones prototypes rather than real objects.

We could have the same feature in JavaScript if we could control the internal proto property of objects. For instance, if we have an Animal object and a Cat object, we could change the proto property of the Cat object so that it links directly to the Animal object (as opposed to linking to a prototype) so that Cat would inherit directly from it.

While the proto property itself is internal and can't be changed, several JavaScript engines have introduced a special object property called __proto__ that's accessible from the JavaScript API. The __proto__ property of an object can be used to change an object's internal proto property, making it possible to directly inherit from another object:

var Animal = {
    name: 'animal',
    eat: function(){
        console.log('The ' + this.name + ' is eating.'),
    }
};

var Cat = {name: 'cat'};
Cat.__proto__ = Animal;

var myCat = {};
myCat.__proto__ = Cat;

myCat.eat(); // 'The cat is eating.'

There are no constructors in this example: Animal and Cat are real objects created using literals. By setting Cat.__proto__ = Animal, we told the interpreter that the internal proto property of Cat should be the Animal object. In the end, myCat inherits from both Cat and Animal and its prototype chain does not have any objects that are prototype. This is a simplified prototypal model that doesn't involve any constructors or prototype but instead relies on setting the prototype chain to use real objects.

You can take a similar approach via Object.create, a new function introduced in ECMAScript 5. It takes a single argument, an object, and creates a new blank object with the internal proto property set to the object passed to it:

var Animal = {
    name: 'animal',
    eat: function(){
        console.log('The ' + this.name + ' is eating.'),
    }
};

var Cat = Object.create(Animal);
Cat.name = 'cat';

var myCat = Object.create(Cat);
myCat.eat(); // 'The cat is eating.'

Notice that Object.create is similar to the clone method from Io, and internally, they actually do the same thing. When we did var Cat = Object.create(Animal), the interpreter created a new object and set its internal proto property to point to the Animal object. We can reproduce the original Io example using Object.create and the results will be strikingly similar:

var Animal = Object.create({});
Animal.name = 'animal';

var Cat = Object.create(Animal);
Cat.name = 'cat';

myCat = Object.create(Cat);

Unfortunately, while both these approaches are nice, they're not available everywhere. The __proto__ property is not a part of the ECMAScript specification, so not all JavaScript engines support it. Object.create, while included in the specs, is from ECMAScript 5—which is less than a year old at the time of writing and isn't implemented in all engines yet. If you need multiple engine support for your programs (especially if it's a web application), keep in mind that these approaches won't work on all platforms.

There is, however, a way to implement Object.create on older engines. Remember that JavaScript objects are used by reference. If you store an object in a variable x and then do y = x, both x and y will now point to the same object. Also, the prototype property of a function is an object and the default object can be overridden by simply assigning a new object to it:

var Animal = {
    name: 'animal',
    eat: function(){
        console.log('The ' + this.name + ' is eating.'),
    }
};

var AnimalProto = function(){};
AnimalProto.prototype = Animal;

var cat = new AnimalProto();
console.log(typeof cat.purr); // 'undefined'

Animal.purr = function(){};

console.log(typeof cat.purr); // 'function'

This should be familiar by now. We created an Animal object with two members, a name property and an eat method. We then created an "intermediate" constructor called AnimalProto and set its prototype property to the Animal object. Because of references, AnimalProto.prototype and Animal both point to the same object, and thus, when we created our cat instance, it was actually inheriting directly from the Animal object—just like an object produced by Object.create.

With this in mind, we can mimic Object.create for JavaScript engines that don't support it:

if (!Object.create) Object.create = function(proto){
    var Intermediate = function(){};
    Intermediate.prototype = proto;
    return new Intermediate();
};

var Animal = {
    name: 'animal',
    eat: function(){
        console.log('The ' + this.name + ' is eating.'),
    }
};

var cat = Object.create(Animal);

console.log(typeof cat.purr); // 'undefined'

Animal.purr = function(){};

console.log(typeof cat.purr); // 'function'

To start, we used if (!Object.create) ... in the first line to check whether Object.create already exists so that we won't overwrite it. Our Object.create function is simple enough: it creates a new constructor called Intermediate and sets this constructor's prototype to the proto object passed. It then returns an instance of this intermediate constructor. Because it uses the features already available in older implementations, our Object.create function is compatible with almost every modern engine.

The Wrap Up

In this chapter we learned all about JavaScript's object implementation and how it differs from other languages. While at its core a prototypal language, JavaScript has features that put it in a different category as a blend of both classical and prototypal languages. We saw how we can create objects using simple literals and constructors with prototypes. We examined inheritance and how JavaScript traverses the prototype chain, and we implemented a simple prototypal model that hides the complexity of prototypes.

Because JavaScript is an object-oriented language at its core, the concepts we've learned here will help us in developing complex programs in JavaScript. And while the mechanics of object-oriented programming are beyond the scope of this book, I hope I've given you enough information to help you explore the topic further.

Now that we've learned how objects work with native JavaScript, we're left to ask a new question: "But what about MooTools?" This is a very interesting question because we didn't see much of MooTools in this chapter. Thankfully, we're just getting started and we still have room to learn one unique way MooTools handles objects. I call it unique because MooTools doesn't just use the concepts presented here to create simple features—it uses them to do something far more dramatic. And that's the subject of our next chapter.

So if you have your cup of tea or coffee ready, I suggest you sit back and relax as we explore MooTools in depth.

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

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