In a class-full object-oriented paradigm, we can inherit a new class based on an old class; we refer to the old class as parent class and the new class as inherited class. With this, objects created from the inherited class get to keep all the properties (data and methods) of the parent class.
Since JavaScript is not class-full, we can instead create new objects directly from an existing (parent) object. To create new objects from a parent object, we can make the prototype of the new object point to the parent object. With this simple step, the new object gets to inherit all the properties (data and methods) of the parent object.
Let us understand this more using a simple example. This example (provided below) defines a function, inheritFromAParentObject(), that takes an old object and returns a new object such that the prototype link of the new object points to the old object. For the sake of brevity, whenever we invoke the printObject() function, we provide its output as a comment.
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Return a trivial inherited object. function inheritFromAParentObject(parentObj) { function tempConstructor() {}; // This is a dummy constructor tempConstructor.prototype = parentObj; return (new tempConstructor()); } // Trivial function to print an object. function printObject (obj) { var i = 0; for (var item in obj) { if (i++ != 0) {elem.innerHTML += ", ";} elem.innerHTML += item + ": " + obj[item]; } } // Define an object. var objFable = {name: "Farmer Giles of Ham", dragon: "Chrysophylax"}; // Print the parent object. printObject(objFable); // Prints "name: Farmer Giles of Ham, dragon: Chrysophylax" // Get a new object and print it. newObjFable = inheritFromAParentObject(objFable); printObject(newObjFable); // Prints "name: Farmer Giles of Ham, dragon: Chrysophylax" // Update the values of the new object. newObjFable.name = "The Hobbit"; newObjFable.dragon = "Smaug"; // Print the new object. printObject(newObjFable); // Prints "name: The Hobbit, dragon: Smaug" // Print the parent object. printObject(objFable); // Prints "name: Farmer Giles of Ham, dragon: Chrysophylax" </script> </html>
Looking at the above example, we can see that when we create a new object using the prototype link, then the new object inherits all properties: functions and non-functions. It is due to this reason that when we print the new object immediately after inheriting it, the values of its non-function properties are identical to that of its parent object.
It is important to note that when we modify the new object, the parent object remains unchanged.
With ECMAScript5, we also have a new method for deriving a new object from a parent object: Object.create(). We can use this method to simply the above example and to avoid the manual assignment of prototype of the parent object to the child object.
Let us rewrite the earlier example (provided below) to use Object.create() -- the output is same as before.
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Trivial function to print an object. function printObject (obj) { var i = 0; for (var item in obj) { if (i++ != 0) {elem.innerHTML += ", ";} elem.innerHTML += item + ": " + obj[item]; } } // Define an object. var objFable = {name: "Farmer Giles of Ham", dragon: "Chrysophylax"}; // Print the parent object. printObject(objFable); // Prints "name: Farmer Giles of Ham, dragon: Chrysophylax" // Get a new object and print it. newObjFable = Object.create(objFable); printObject(newObjFable); // Prints "name: Farmer Giles of Ham, dragon: Chrysophylax" // Update the values of the new object. newObjFable.name = "The Hobbit"; newObjFable.dragon = "Smaug"; // Print the new object. printObject(newObjFable); // Prints "name: The Hobbit, dragon: Smaug" // Print the parent object. printObject(objFable); // Prints "name: Farmer Giles of Ham, dragon: Chrysophylax" </script> </html>
A new inherited object can add properties of its own. This can sometimes lead to a requirement for retrieving only those properties that have been added by the derived object explicitly and to exclude those properties that were inherited form the prototype. For such cases, JavaScript provides two methods: Object.getOwnPropertyNames() and Object.keys().
There is an important difference between the above two methods. Object.getOwnPropertyNames() prints all non-inherited properties, whether enumerable or non-enumerable. Object.keys() prints all non-inherited properties, but only those that are enumerable!
We provide a simple example that uses Object.getOwnProperty() and Object.keys() to filter only those properties that are added explicitly by the derived object. To show that Object.keys() only prints enumerable properties, we make the "hero" property of the new object, newObjFable non-enumerable; since "hero" property is non-enumerable, Object.keys() skips this property.
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Define an object var objFable = {name: "The Hobbit", dragon: "Smaug"}; // Get a new object newObjFable = Object.create(objFable); // Add two new properties to the new object newObjFable.type = "Fable"; newObjFable.hero = "Giles"; Object.defineProperty(newObjFable, "hero", {enumerable: false}); // Object.getOwnPropertyNames() prints own (non-inherited) // properties (enumerable or non-enumerable). elem.innerHTML += Object.getOwnPropertyNames(newObjFable); // Prints "type, hero" // Object.keys() prints own (non-inherited) properties // but only those that are enumerable. elem.innerHTML += Object.keys(newObjFable); // Prints "type" // for/in loop prints all enumerable properties (own or inherited) for (item in newObjFable) {elem.innerHTML += item+" ";} // Prints "type name dragon" </script> </html>
So, this begs a question: how does one print all properties of an object irrespective of whether they are own or inherited, whether they are enumerable or non-enumerable.
One way to print all properties of the object is to use Object.getOwnPropertyNames() and then use JavaScript's Object.getPrototypeOf() method to find its parent -- once we locate the parent, we can again call Object.getOwnPropertyNames() to get all properties of the parent. Next, we repeat the steps for parent of the parent. Thus, we can recursively walk the object inheritance chain till we reach the Object.prototype at which point we would stop since Object.prototype is the ultimate object with its parent being NULL.
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Return property descriptors of the passed property. function getPropAttributes (obj, prop) { var x = "", i = 0; attr = Object.getOwnPropertyDescriptor(obj, prop); for (var item in attr) { if (i++ != 0) {x += ", ";} if ((typeof obj[prop]) == "function") { if (item != "value") { x += (item + ": " + attr[item]); } } else { x += (item + ": " + attr[item]); } } return x; } // Print all non-inherited properties (enumerable/non-enumerable) properties. function printObjectRecursively(obj) { var prop = Object.getOwnPropertyNames(obj); for (var item in prop) { elem.innerHTML += prop[item]+ " -- "+ getPropAttributes(obj, prop[item]) + "<br>"; } if (obj = Object.getPrototypeOf(obj)) { elem.innerHTML += "<br>Print a parent object<br>"; printObjectRecursively(obj); } } // Define an object. var objFable = {name: "The Hobbit", dragon: "Smaug"}; // Get a new object and add two properties to it. newObjFable = Object.create(objFable); newObjFable.type = "Fable"; newObjFable.hero = "Giles"; Object.defineProperty(newObjFable, "hero", {enumerable: false}); printObjectRecursively(newObjFable); </script> </html>
The above program uses Object.getOwnPropertyNames() to print properties of each object. It is important to note that we should note use a for/in loop when printing properties recursively since with a for/in loop, all properties (only enumerable) are printed whether they are own or inherited. Due to this, a property belonging to a parent would not only be listed for that parent but would also be listed for all child nodes in the inheritance hierarchy. This can lead to duplicate entries!
To avoid verbose output, we do not print the value attribute of method properties (they have a type of "function"). Here is the output of the above program. From this output, we can see that newObjFable has two objects in its inheritance chain: objFable and Object.prototype. Note that the output prints all properties even if some of them have their enumerable attribute set to false!
Please note that properties added to inherited objects would not be reflected as part of the parent object, unless we explicitly add them to its parent prototype object.