5

I've been trying to get to grips with OO JavaScript and created a simple example.

function BasePage(name) {
    this.init(name);
}

BasePage.prototype = {
    init: function(name) {
       this.name = name; 
    },
    getName: function() {
        return this.name;
    }
}

function FaqPage (name, faq) {
    this.init(name, faq);
}

FaqPage.prototype = new BasePage();

FaqPage.prototype = {
    init: function(name, faq) {
        BasePage.prototype.init.call(this, name);
        this.faq = faq; 
    },
    getFaq: function() {
        return this.faq;
    }
}

var faqPage = new FaqPage('Faq Page', 'Faq');

var text = faqPage.getName() + ' ' + faqPage.getFaq();
$('body').text(text);

The result of running this results in the following message:

Uncaught TypeError: Object #<Object> has no method 'getName'

What I would like to know is how can I call the method getName() in the super class without having to override and call it in the sub class?

Also if my if you think this approach is bad/good.

4 Answers 4

6

That error occurs because even though you've set FaqPage's prototype to be an instance of BasePage, your next line immediately overwrites that. So FaqPage is not inheriting from BasePage. Add properties/methods to FaqPage's prototype instead of defining it a second time:

FaqPage.prototype.init = function(name, faq) {
    BasePage.prototype.init.call(this, name);
    this.faq = faq; 
}
FaqPage.prototype.getFaq = function() {
    return this.faq;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, I had a feeling that would be the reason. Is this the only way/recommended way to add methods in OO JS? For me coming from C# it seems ugly not being able to group class methods in one {scope}
I know what you mean but yes this is the recommended way. It may grow on you. Keep in mind you can place methods on BasePage as well.
2

The first line is right, you create a prototype from the base object.

FaqPage.prototype = new BasePage();

But then you override it, so is does not inherit the base object anymore

FaqPage.prototype = {
    init: function(name, faq) {
        BasePage.prototype.init.call(this, name);
        this.faq = faq; 
    },
    getFaq: function() {
        return this.faq;
    }
}

Instead of defining the entire prototype, override just individual functions:

FaqPage.prototype.init = function(name, faq) {
    BasePage.prototype.init.call(this, name);
    this.faq = faq; 
};

FaqPage.prototype.getFaq = function() {
    return this.faq;
};

Comments

1

I feel your pain. As others have mentioned getName is undefined because you override the prototype of FaqPage. Hence I'll not reiterate that explanation.

That being said I do agree that it's good to encapsulate prototype methods in a single scope. Perhaps you should use a JavaScript library for this purpose. The smallest one out there is augment. In fact it's only 17 lines long:

Function.prototype.augment = function (body) {
    var base = this.prototype;
    var prototype = Object.create(base);
    body.apply(prototype, Array.from(arguments, 1).concat(base));
    if (!Object.ownPropertyOf(prototype, "constructor")) return prototype;
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
};

(function funct() {
    var bind = funct.bind;
    var bindable = Function.bindable = bind.bind(bind);
    var callable = Function.callable = bindable(funct.call);
    Object.ownPropertyOf = callable(funct.hasOwnProperty);
    Array.from = callable([].slice);
}());

Here's how your code would look if you used augment:

var BasePage = Object.augment(function () {
    this.constructor = function (name) {
        this.name = name;
    };

    this.getName = function () {
        return this.name;
    };
});

var FaqPage = BasePage.augment(function (base) {
    this.constructor = function (name, faq) {
        base.constructor.call(this, name);
        this.faq = faq;
    };

    this.getFaq = function () {
        return this.faq;
    };
});

Then you may use it as you normally would:

var faqPage = new FaqPage("Faq Page", "Faq");
var text = faqPage.getName() + " " + faqPage.getFaq();
$("body").text(text);

Hope that helps.

5 Comments

Thanks Aadit, I see Augment faired well in the benchmark you did, but what happened in Chrome? Graph showed zero operations?
@Stokedout - Benchmarks should be taken with a grain of salt. No doubt augment is fast. However most JavaScript programmers are amateurs who write sloppy code. To compensate for this most JavaScript engines like V8 heavily optimize it. The result is that even bad code runs blazingly fast (sometimes even faster than good code because bad design patterns are well optimized). However that's no excuse to write bad code. Good code is not only fast but also readable, expressive, and easy to understand and maintain; and augment hits all these points. It's actually fast in Chrome 26. Slow in 27.
Cool thanks, my reasons for changing to OO JS are all what you've just mentioned. I can't keep up with Chrome releases and can't wait to get shot of IE releases ;-). One final comment about the above code. If I use constructor then surely init is a duplication of the same thing?
@Stokedout - Yes, indeed. Your init method is redundant. That code should be moved into the constructor method. It's important that the constructor function be named constructor and nothing else. This is because all prototype objects in JavaScript have a constructor property. Hence it makes sense to use constructor as the name of the constructor function as this simplifies code. I made changes to my answer to reflect this. You should read more about the constructor property and prototypal inhertance in JavaScript here: stackoverflow.com/a/8096017/783743 Hope that helps. =)
Checked out the post on constructor, wow you really do know your JS ;-) Thanks for your help on this one
1

If inheritance is what you are after, it's not very straightforward to accomplish in JavaScript as far as I know. I've used this snippet by John Resig and it works like a charm. http://ejohn.org/blog/simple-javascript-inheritance/

In your case this is how it would work,

BasePage

var BasePage = Class.extend({
  init: function(name){
     this.name = name;
  },
 getName: function(){
     return this.name;
 }
});

FaqPage class inheriting BasePage

var FaqPage = BasePage.extend({
  init: function(name, faq){
    this._super( name );
    this.faq = faq;
  },
  getFaq: function() {
    return this.faq;
  }
});

And then

var faqPage = new FaqPage('Faq Page', 'Faq');
var text = faqPage.getName() + ' ' + faqPage.getFaq();
$('body').text(text);

Of course, for this to work you'll have to include the implementation code on the page I linked.

Here's the fiddle, http://jsfiddle.net/c8yjY/

1 Comment

John Resig's code is a tad bit outdated. There are lots of better OOP libraries out there: jsperf.com/oop-benchmark/127 Please read my answer above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.