Prototype-based classes in JavaScript and Node.js bring a high level of flexibility to the software architecture. Since objects and classes are fully editable at any point in program operation, the functionality can be extended to implement object-level inheritance at runtime.
For a little background information, JavaScript does not explicitly implement traditional classes. Most object-oriented programming languages, such as C++ and Java, define strict classes with properties and methods. The classes are then instantiated as objects. Objects of the same class will have the same set of properties and methods, and will generally be compatible with each other.
JavaScript, on the other hand, does not enable user creation of classes. Over the years, zealous and creative programmers have extended the “Prototype” functionality of JavaScript to create the equivalent of classes through functions:
//Class Definition
function Book(_Title, _Author){
this.Title = _Title;
this.Author = _Author;
this.Pages = new Array();
}
Book.prototype.getNumPages = function(){ return this.Pages.length; }
//Object Instantiation
var odyssey = new Book("Odyssey","Homer");
odyssey.Pages.push("Tell me, o muse, of that ingenious hero who travelled far...");
odyssey.Pages.push("So saying she bound on her glittering golden sandals, imperishable...");
console.log(odyssey.getNumPages());
The class definition uses a function to return an object with properties. The prototype capability is then leveraged to create methods. This implementation is generally considered the most efficient and flexible, since otherwise defining methods in the function body would require recreating the methods on each object instantiation.
With the object properly defined, its use is similar to most other class-based languages. The unique feature of JavaScript, however, is that objects can be mutated and transformed very easily:
delete odyssey.Pages;
odyssey.Pages = new Array();
The delete command actually removes the entire property from the object, making it seem as if it never existed. Adding it back is similarly a one-line command.
This flexible infrastructure enables inheritance at the object level. Similar to CSS-classes that inherit the values of their parent elements, Node.js objects can be made to inherit the values of other objects. Take the following example:
var CarOptions = {
"Color":"Blue",
"Drivetrain":"2WD",
"Interior":"Cloth",
"AirCondition":0
};
var myCarOptions = {
"Color":"Red"
};
for(var prop in defaultCarOptions){
if(!(prop in myCarOptions)) myCarOptions[prop] = defaultCarOptions[prop];
}
console.log(JSON.stringify(myCarOptions));
The myCarOptions object is made to inherit the non-overridden settings of defaultCarOptions. Since objects can be treated as associative arrays, implementing object inheritance is as simple as iterating through the properties and copying the non-overridden settings. Although the example implements a shallow copy, this can easily be converted to a full copy through use of a recursive function.
Where this technique can become interesting is in recursively structured objects, where each object links to its immediate parent object. Resolving an object will then recursively copy the non-assigned attributes from each parent. Alternatively, instead of simply ignoring parent properties when the child property exists, certain property values could be concatenated or otherwise processed based on internal logic.
Written by Andrew Palczewski
About the Author
Andrew Palczewski is CEO of apHarmony, a Chicago software development company. He holds a Master's degree in Computer Engineering from the University of Illinois at Urbana-Champaign and has over ten years' experience in managing development of software projects.
Google+