0


I am new to js OO programming and I can't find the solution to this error. I am declaring the following class hierarchy:

function FML_Field(id){
    this.id= id;
    this.optional= true;
    this.node= null;

    if(this.id === undefined){
        throw ""; //should provide Id;
    }

    var this.node= document.getElementById(this.id);
    if(this.node === null){
        throw "";
    }

    this.setAsOptional= function(){
        this.optional= true;
   };
    this.setAsRequired= function(){
         this.optional= false;
    };
    this.isOptional= function(){
        return this.optional;
    };
}

and its son:

function FML_Text(id){
    this.prototype= new FML_Field(id);
    FML_Text.prototype.constructor= FML_Text;
    this.maxLength= false;
    this.minLength= false;

    this.setMaxLength= function(maxLength){
        this.maxLength= maxLength;
    }
    this.getMaxLength= function(){
        return this.maxLength;
    }
    this.hasMaxLength= function(){
        return this.maxLength !== false;
    }
}

then I proceed with the following code:

var first_name = new FML_Text("first_name");
first_name.setAsRequired(); /*throws an error: setAsRequired is not defined*/

What's wrong? I've checked with the javascript console: first_name is defined but setAsRequired() isn't. The following function calls like first_name.setMaxLength() have no problems.

Thank you in advance.

Thank you in advance

  • May I point you to this StackOverflow answer? I'm sure it will help you understand more the JavaScript OOP. It's really good and exaustive: http://stackoverflow.com/questions/1595611/how-to-properly-create-a-custom-object-in-javascript – dmarucco Apr 22 '11 at 08:44

1 Answers1

0

This isn't how you set up inheritance:

function FML_Text(id){
    this.prototype= new FML_Field(id);
    // ...
}

All that does is create a property called prototype on the instance.

It's done like this:

function FML_Text(id){
    // ...
}
FML_Text.prototype = new FML_Field();

...and you can't pass the id argument into it, because it happens before the child object constructor is called. Instead, the usual thing is to define an "initializer" function that each level in your hierarchy supports post-construction, and call that.

That's the basics of it, anyway, but really robust inheritance requires more work. For instance, actually making a call to the parent's version of a function that the child also has defined (e.g., a common initializer, or any time a child specializes a parent method) is actually a bit of a fiddle in JavaScript, and there are some other "gotchas" as well. But with a bit of plumbing, you can get very effective inheritance chains (including passing construction-time arguments to parent initializers).

Rather than flying solo, you might want to use one of the existing implementations of this stuff. I describe mine in this article from a while back, which features really efficient calls to parent methods ("supercalls"). The Prototype library also provides an effective "class" system although it was issues with the performance (and compatibility) of that system that lead me to do the article above. Dean Edwards has also written extensively on this subject, and John Resig has pitched in. I had issues with both of those when I looked at them a couple of years ago, but there may have been updates.

Things to look for (in my opinion):

  • Straightforward, declarative syntax.
  • Syntax that's very friendly to your class having private static members for implementation stuff that doesn't need to be public. (Private instance methods can be done in just about any system, but they're expensive; see Crockford's discussion of them, and my comparison of various ways to achieve them.)
  • The system should not rely on function decompilation (using the toString method on Function instances). Both Prototype's and Resig's do, I don't know about Edwards' as does Edwards'. Function decompilation has never been standardized and doesn't work on some mobile browsers. (In Resig's and Edwards' versions, the toString call is implicit, and so it's a bit hard to find, but it's there: They pass a function instance into a regex test, which will implicitly call the function's toString.)
  • The system should not create new function objects on-the-fly when calls to instance methods are made, only when classes are defined (if then). Prototype's does, every time you call an instance method that may need to call its parent's version (their magic $super argument). Their mechanism makes it dead easy to use the parent's version, but at the cost (again) of creating a new function on every call whether you actually call $super or not.
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thank you very much, it's a very good and complete answer. I'll look more into OOP javascript, which looks like a real mess! – Traveling Saleswoman Apr 22 '11 at 10:10
  • @travelingsaleswoman: No worries, glad that helped. I wouldn't say it was a mess. JavaScript has very powerful, and flexible, OOP features; that flexibility can sometimes be challenging. And until recently, there was a real problem with calling the parent's versions of methods. The 5th edition of ECMAScript (JavaScript), just 18 months old now, adds a couple of things that may supercalls a *little* easier, but it'll take time for implementations to catch up to the spec. I'll be updating my article to talk about those at some point (not hyper-soon). – T.J. Crowder Apr 22 '11 at 10:27
  • Coffeescript provides traditional OOP inheritance model, you may want to consider porting to coffeescript or perhaps examining the generated output for its class definitions – timoxley Oct 13 '11 at 12:06