/* Person Prototype Object. */
var Person = {
name: ‘default name’,
getName: function() {
return this.name;
}
};
Instead of using a constructor function named Person to define the class structure, Person
is now an object literal. It is the prototype object for any other Person-like objects that you want
to create. Define all attributes and methods you want these objects to have, and give them
default values. For the methods, those default values will probably not be changed; for attrib-
utes, they almost certainly will be:
var reader = clone(Person);
alert(reader.getName()); // This will output ‘default name’.
reader.name = ‘John Smith’;
alert(reader.getName()); // This will now output ‘John Smith’.
To create Author, you don’t make a subclass of Person. Instead you make a clone:
/* Author Prototype Object. */
var Author = clone(Person);
Author.books = []; // Default value.
Author.getBooks = function() {
return this.books;
}
The methods and attributes of this clone can then be overridden. You can change the
default values given by Person, or you can add new attributes and methods. That creates a new
prototype object, which you can then clone to create new Author-like objects:
var author = [];
author[0] = clone(Author);
author[0].name = ‘Dustin Diaz’;
author[0].books = [’JavaScript Design Patterns’];
author[1] = clone(Author);
author[1].name = ‘Ross Harmes’;
author[1].books = [’JavaScript Design Patterns’];
author[1].getName();
author[1].getBooks();
var authorClone = clone(Author);
alert(authorClone.name); // Linked to the primative Person.name, which is the
// string ‘default name’.
authorClone.name = ‘new name’; // A new primative is created and added to the
// authorClone object itself.
alert(authorClone.name); // Now linked to the primative authorClone.name, which
// is the string ‘new name’.
authorClone.books.push(’new book’); // authorClone.books is linked to the array
// Author.books. We just modified the
// prototype object’s default value, and all
// other objects that link to it will now
// have a new default value there.
authorClone.books = []; // A new array is created and added to the authorClone
// object itself.
authorClone.books.push(’new book’); // We are now modifying that new array.
var CompoundObject = {
string1: ‘default value’,
childObject: {
bool: true,
num: 10
}
}
Sometimes prototype objects will have child objects within them. If you want to override
a single value within that child object, you have to recreate the entire thing. This can be done
by setting the child object to be an empty object literal and then recreating it, but that would
mean that the cloned object would have to know the exact structure and defaults for each
child object. In order to keep all objects as loosely coupled as possible, any complex child
objects should be created using methods:
var compoundObjectClone = clone(CompoundObject);
// Bad! Changes the value of CompoundObject.childObject.num.
compoundObjectClone.childObject.num = 5;
// Better. Creates a new object, but compoundObject must know the structure
// of that object, and the defaults. This makes CompoundObject and
// compoundObjectClone tightly coupled.
compoundObjectClone.childObject = {
bool: true,
num: 5
};
In this example, childObject is recreated and compoundObjectClone.childObject.num is
modified. The problem is that compoundObjectClone must know that childObject has two
attributes, with values true and 10. A better approach is to have a factory method that creates
the childObject:
// Best approach. Uses a method to create a new object, with the same structure and
// defaults as the original.
var CompoundObject = {};
CompoundObject.string1 = ‘default value’,
CompoundObject.createChildObject = function() {
return {
bool: true,
num: 10
}
};
CompoundObject.childObject = CompoundObject.createChildObject();
var compoundObjectClone = clone(CompoundObject);
compoundObjectClone.childObject = CompoundObject.createChildObject();
compoundObjectClone.childObject.num = 5;
/* Clone function. */
function clone(object) {
function F() {}
F.prototype = object;
return new F;
}
