JavaScript uses prototype-based object oriented programming, where behavior reuse (called inheritance in classical OOP) is handled by cloning the prototypes of existing objects. Therefore JavaScript does not have traditional classes or class-based inheritance. However, since inheritance is so powerful and needed to build scalable/extendable JavaScript libraries, that YUI implemented Pseudo-Classical Inheritance with the YAHOO.lang.extend
function. This recipe explains how to use YAHOO.lang.extend
.
First create the Parent
function (sometimes called a 'class') and augment its prototype:
YAHOO.test.Parent = function(info) { alert("Parent: " + info); }; YAHOO.test.Parent.prototype = { testMethod: function() { alert("Class: Parent"); } };
Here is how Parent
is instantiated and used:
var parentInstance = new YAHOO.test.Parent(1234); alert(parentInstance.testMethod()); // alerts 'Class: Parent'
Now create the Child
function (sometimes called a 'subclass') and use extend to have it inherit from the Parent
function:
YAHOO.test.Child = function(info, arg1, arg2) { // chain the constructors YAHOO.test.Child.superclass.constructor.apply( this, arguments); // or YAHOO.test.Child.superclass.constructor.call( this, info, arg1, arg2); };
Next, have the Child
extend the Parent
function. This must be done immediately after the Child
constructor, before modifying the prototype
of Child:
YAHOO.lang.extend(YAHOO.test.Child,YAHOO.test.Parent, { testMethod: function() { alert("Class: Child"); } });
You can also manually modify the prototype
of Child
after the extension, without affecting the Parent:
YAHOO.test.Child.prototype.parentTestMethod = function() { return YAHOO.test.Child.superclass.testMethod.call(this); };
Here is how a child is instantiated and used:
var childInstance = new YAHOO.test.Child("constructor executed"); childInstance.testMethod("testMethod invoked"); // alerts: 'Child: testMethod invoked' childInstance.parentTestMethod("testMethod invoked"); // alerts: 'Parent: testMethod invoked'
The YAHOO.lang.extend
function requires a Child
and a Parent
function as the first and second arguments. The third argument is optional, but is an option object containing additional properties and functions to apply to the prototype
property of the Child
function. Be aware that the prototype
property of the Child
function will be overwritten by YAHOO.lang.extend
, while the Parent
function will remain unmodified.
Under the hood, the YAHOO.lang.extend
function instantiates a constructor-less clone of the Parent
function and assigns the instantiated object to the prototype
property of the Child
function. In this way the prototype
of the Child
function will be a separate object from the prototype
of the Parent
function, but still reference the same properties and functions as the Parent
function. Therefore modifying the prototype
of the Child
function does not affect the prototype
of the Parent
function.
The Parent
function is set to a special property, superclass
, which is attached directly to the Child
constructor function. Therefore, you can access any function on the Parent
function that may have been overwritten by the Child
function. In this recipe, superclass
was used in the constructor of the Child
function and one of its functions. In the Child
constructor the Parent
function constructor is called and executed (YAHOO.test.Child.superclass.constructor
). Anytime you use superclass
to access the Parent
function, you need to use either the call
or apply
functions so the parent function executes using the current execution context (this
).
In the parentTestMethod
function, superclass
is called to fetch and execute the testMethod
from the Parent
function, even though it had been overwritten by the Child
function.
You can specify additional prototype
properties of the Child
function by using the third argument of YAHOO.lang.extend
, but you are not required to. At any point after calling YAHOO.lang.extend
, you may alter the prototype
property of the Child
function, as shown in the recipe by the creation of the parentTestMethod
function. Modifying the prototype of the Child
function will not affect the Parent
function.