views:

538

answers:

5

I know there is a lot of similar questions are tons of great answers to this. I tried to look at the classical inheritance methods, or those closure methods etc. Somehow I consider they are more or less "hack" methods to me, as it doesn't really what the javascript is designed to do. (Welcome anybody correct me if I am wrong). OK, as long as it works, I satisfy with the classical inheritance pattern like:

PARENTClass = function (basevar) { do something here; };
PARENTClass.prototype = { a: b, c: d}; // prototype is auto gen 

// Inheritance goes here
CHILDClass = function (childvar) { do something; };
CHILDClass.prototype = new PARENTClass(*1); // Actual inheritance to the prototype statement
// Instance
CHILDInstance = new CHILDClass(whatever);

Above is somehow, to my understanding the inheritance of JS. But one scenario I have no idea how to implement, is that what if I want to do some initializing DURING object creation (ie, within constructor), and the new object can be used right away.... My illustration on problem might not be too clear, so let me use the following C# Psuedo to explain what I want to do:

class PARENT {
    public PARENT (basevar) { ... }
}
class CHILD : PARENT {
    public CHILD (basevar) : PARENT (basevar) // constructor of child, and call parent constructor during construct.
    { ... }
}

For some reason (like init. UI elements), putting them in constructor seems the best way to do. Anyone have idea on how can I do it.

PS: in the *1, I have no idea what I should put there. PS2: The above situation I DID found the jquery.inherit library can do, I just wonder if not using library can achieve it. PS3: Or my understanding is wrong. Since javascript is not intended to mimick OOP (that's why i call it hack), what is the "CORRECT" logic to implement this.

+4  A: 

You can manually invoke the parent constructor in the subclass constructor like this:

CHILDClass = function (basevar) {
  PARENTClass.call(this, basevar);
  // do something;
};

The trick here is using the call method, which allows you to invoke a method in the context of a different object. See the documentation of call for more details.

Ayman Hourieh
+6  A: 

It is not a hack as such; JavaScript is a prototyped language, as defined by Wikipedia as where:

..classes are not present, and behavior reuse (known as inheritance in class-based languages) is performed via a process of cloning existing objects that serve as prototypes.

As it says, classes are not used in JavaScript; each object that you create is descended from the JavaScript Object; all objects in JavaScript have the prototype object, and all instances of objects you create 'inherit' methods and properties from their object's prototype object. Take a look at the MDC prototype object reference for more information.

As of this, when you call the line:

CHILDClass.prototype = new PARENTClass();

This allows the CHILDClass object to add methods and properties to its prototype object from the PARENTClass object, which creates an effect similar to the idea of inheritance present in class-based languages. Since the prototype object affects every instance created of that object, this allows the parent object's methods and properties to be present in every instance of your child object.

If you want to call your parent class's constructor in your child class's constructor, you use the JavaScript call function; this allows you to call the parent class's constructor in the context of the child class's constructor, therefore setting the newly prototyped properties in your child class to what they are set as in the parent class.

You also do not need to put anything where you have specified the *1, since that line is merely used to add the methods and properties to the child class's prototype object; however, bear in mind that it calls the parent class's constructor, so if there are any arguments that are fundamental in the operation of the parent class constructor, you should check that these are present so as to avoid JavaScript errors.

Perspx
Nice and comprehensive answer. Just one more thing to add.. In case, for example, when calling the constructor of the parent, something, which might be fairy expensive (like reading from XML) or user-aware (like calling alert) will be executed. Even I put nothing in the *1, that basically still would be executed; Unless I explicitly put checking in the parent (for instance, if (arguments.length ==0) return; ). Is it a "proper" solution to this? Sorry that I came from a pure strict OOP background, I still not really get used to js oriented programming logic.
xandy
Yes, that's how I'd do it - if someone creates an instance of your object that requires the arguments then you don't want to perform those actions anyway.
Perspx
+1  A: 

JavaScript has no built-in support for inheritance hierarchies as type extension is supposed to be done via aggregation, ie adding desired functionality directly to the object itself or its prototype if the property is to be shared between instances.

Nevertheless, JS is powerful enough to make implementing other forms of object construction possible, including classical inheritance.

Given a clone function - which is enough to add 'true' prototypical inheritance, and not JavaScript's bastardization thereof - your exampe can be implemented like this:

function ParentClass(baseVar) {
    // do stuff
}

// don't overwrite the prototype object if you want to keep `constructor`
// see http://joost.zeekat.nl/constructors-considered-mildly-confusing.html
ParentClass.prototype.a = 'b';
ParentClass.prototype.c = 'd';

function ChildClass(childVar) {
    // call the super constructor
    ParentClass.call(this, childVar);
}

// don't inherit from a ParentClass instance, but the actual prototype
ChildClass.prototype = clone(ParentClass.prototype);
ChildClass.prototype.e = 'f';

It's also possible to add some syntactic sugar for class-based inheritance - my own implementation can be found here.

The example from above would then read

var ParentClass = Class.extend({
    constructor: function(baseVar) {
     // do stuff
    },
    a: 'b',
    c: 'd'
});

var ChildClass = ParentClass.extend({
    e: 'f'
});
Christoph
+1  A: 

I've got a lightweight javascript OOP wrapper that provides 'Class-like' inheritance where you can override base methods or call base constructors or members.

You define your classes like this:

//Define the 'Cat' class
function Cat(catType, firstName, lastName)
{
    //Call the 'Animal' constructor.
    Cat.$baseNew.call(this, firstName, lastName);

    this.catType = catType;
}
//Extend Animal, and Register the 'Cat' type.
Cat.extend(Animal, { type: 'Cat' }, {
    hello: function(text)
    {
        return "meaoow: " + text;
    },
    getFullName: function()
    {
        //Call the base 'Animal' getFullName method.
        return this.catType + ": " + Cat.$base.getFullName.call(this);
    }
})

//It has a built-in type system that lets you do stuff like:

var cat = new Cat("ginger", "kitty", "kat");
Cat.getType()                     // "Cat"
cat.getBaseTypesAndSelf()         // ["Cat","Animal","Class"]
cat.getType()                     // "Cat"
cat.isTypeOf(Animal.getType())    // "True"

var dynamicCat = Class.createNew("Cat", ["tab","fat","cat"])
dynamicCat.getBaseTypesAndSelf()  // ["Cat","Animal","Class"]
dynamicCat.getFullName()          // tab: fat cat

source code available at: Class.js

I also have more details in my blog post about OOP in javascript

mythz
A: 

Just thought I'd mention some of the issues with the classical pattern you're going for:

  1. Reference vars on the super class(es) will be available as essentially statics on ALL instances. For example, if you have var arr = [1,2,3] in the super, and do instance_1.arr.push(4) instance_2.arr.push(5) ALL of these instances will "see" the changes.
  2. So you solve 1. with Ayman's solution which Zakas calls "Constructor Stealing", but now you call the constructor twice: once for your prototype and once for the constructor stealing. Solution - for your prototype use a helper like inheritPrototype (I showed the whole implementation of this in this post: inheritPrototype method FWIW, this essentially came from a combination of page 181 of Zakas's book and some Crockford study.

  3. No privacy (but then again, you'd need to use something like the Durable Object pattern to get this and that may not be what you want)

  4. Object definition is left "dangling": Solution - put an if statement checking for any of your prototype's functions and then define the prototype with a prototype literal.

I have running examples of all of this on github!!!

It's been just as much of a challenge for me to truly grok both: Zakas and Crockford books on object creation and inheritance. I also needed to try some different JavaScript TDD frameworks. So I decided to create an essay on both TDD Frameworks and JavaScript Object Creation & Inheritance. It has running code and jspec tests! Here's the link:* My GitHub Open Source Essay/Book

Rob