4

I've been reading the chapter on functional inheritance in Crockford's 'The Good Parts'. In the mammal example he gives I'm a bit confused as to why he uses the superior method to modify the get_name function. Here is the example in question:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

var mammal = function (spec) {
    var that = {};

    that.get_name = function () {
        return spec.name;
    };

    that.says = function () {
        return spec.saying || '';
    };

    return that;
};

var myMammal = mammal({
    name: 'Herb'
});

var cat = function (spec) {
    spec.saying = spec.saying || 'meow';
    var that = mammal(spec);

    that.purr = function (n) {
        var i, s = '';
        for (i = 0; i < n; i += 1) {
            if (s) {
                s += '-';
            }
            s += 'r';
        }
        return s;
    };

    that.get_name = function () {
        return that.says() + ' ' + spec.name + ' ' + that.says();
    };

    return that;
};

Object.method('superior', function (name) {
    var that = this,
        method = that[name];
    return function () {
        return method.apply(that, arguments);
    };
});

var coolcat = function (spec) {
    var that = cat(spec);
    var super_get_name = that.superior('get_name');

    that.get_name = function (n) {
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};

var myCoolCat = coolcat({
    name: 'Bix'
});

var name = myCoolCat.get_name(); // 'like meow Bix meow baby'

I'm confused about this because I can replicate the same thing by removing the superior method and just changing coolcat as follows:

var coolcat = function(spec) {
    var that = cat(spec);
    var super_get_name = that.get_name();

    that.get_name = function(n) {
        return 'like ' + super_get_name + ' baby';
    };
    return that;
};

So, I don't understand why Crockford chooses to use the superior method. Is anyone able to explain at all?

4
  • 6
    Wow, is Crockford really suggesting doing this? It's a misguided not-very-readable way to get superclass method calls IMO. To avoid using the real language feature of prototype inheritance that hard, and then to spoil it all by prototyping onto Object of all things? Ouch. Commented Mar 10, 2012 at 19:12
  • @bobince: That code doesn't prototype onto Object; rather, it prototypes onto Function, which makes it available on Object (since Object is a constructor-function). (Though I must admit, I'm also pretty surprised by this suggestion. It seems like it would be clearer to write var super_get_name = that.get_name. That would force the new get_name to write super_get_name.apply(that) instead of just super_get_name(), but that doesn't seem like a big deal to me.) Commented Mar 10, 2012 at 19:36
  • 3
    @ruakh: The call to method() is adding the member superior onto Object.prototype. Commented Mar 10, 2012 at 19:44
  • @bobince: Oh, yes, you're right. O.K., yeah, that's pretty terrible. :-P Commented Mar 10, 2012 at 19:45

2 Answers 2

4

The idea here is that this:

var super_get_name = that.superior('get_name');

makes super_get_name into a function that — every time it is called — invokes that's original get_name method. This allows the new get_name to call the old (super-class) get_name.

Now, if the original get_name method will never have any effect other than to return a single value that never changes, then yeah, this is kind of pointless; you can just save that single-value-that-never-changes and then use it in the new get_name. But if the original get_name can actually do things (such as, say, run an AJAX request, or change the styling of an HTML element), or if its return-value can change (say, if there were some corresponding set_name method), then there would be an important difference between what your code does (save the original return-value and use it) and what Crockford's code does (save the original method and invoke it).

Sign up to request clarification or add additional context in comments.

2 Comments

thanks for your response. I'm still a bit confused. If I write var super_get_name = that.get_name() won't I be getting the latest return value anyway? So, why does it matter that the new get_name can call the old get_name?
@StephenYoung: If you write var super_get_name = that.get_name(), then the old get_name() will be called when you originally create the coolcat, and will not be called when you later call myCoolcat.get_name(). So myCoolcat.get_name() won't have the latest return-value, but rather, the original return-value.
3

The confusion that arises from this chapter of Crockford's book arises as what Crockford describes is "his" preferred pattern for implementing inheritance in JavaScript, which relies on his extending the Function object with the Function.prototype.method (chapter 1.3) which he uses to add methods to the Function object.

The problem addressed in the coolcat example is the need to access the method of the parent type. In 'classical' OO languages like Java this is natural as classes exist in their own right. In JavaScript inheritance is prototypical, you make an object of type mammal and then modify the object to create the type cat or coolcat.

Depending on your design you may add properties and functions or override an 'inherited' function. The problem arises when you override an 'inherited' function, in JavaScript you basically replace the old function with the new function, thereby loosing the older function.

Crockford now needs to do two things:

  1. get the parent's (cat's) get_name method; and
  2. save it in a manner that can be used from within the overridden method.

In this code:

var coolcat = function(spec) {
    var that = cat(spec), 
        super_get_name = that.superior('get_name');
    that.get_name = function(n) {
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};

He does 1. by calling the superior method to get a function that gets the cat's get_name function; and he does 2. by saving it to the super_get_name variable within the coolcat function(/object) allowing access to the cat's get_name function before it is overridden (more correctly overwritten) by the coolcat's get_name function.

In my opinion the confusion arises because:

  1. The superior method is named oddly: the 'superior' method is simply a function look up by name method and could be better named, for example as getFunctionByName (you can try this by replacing the string get_name, by purr, the coolcat's get_name will now call purr, just remember to call it as super_get_name(10) otherwise you'll get an empty string back).
  2. Secondly the code and the pattern obfuscates the code by relying on some particular Crockford patterns, and is likely to stresses you out if you attempt to dive into this chapter without having followed the entire book.

I think there is a simpler way to achieve this, one that I think because it is completely localized is easier to understand etc., as in the code below:

var coolcat = function(spec) {
    var that = cat(spec);
    that.parent_get_name = that.get_name;
    that.get_name = function() {
       return 'like ' + this.parent_get_name() + ' baby';
    };
    return that;
};

There are some other oddities for example the argument n in definition of the coolcat get_name function, which I can only assume came from copying the purr function, which would suggest a ghost writer!

Finally, I would suggest that before one reads the book one should listen to his talk on 'JavaScript the good parts'. The talk is absolutely brilliant, much better than the book.

Comments

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.