简体   繁体   中英

Using super methods in Javascript based on Crockford's functional inheritance

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?

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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM