简体   繁体   中英

Understanding the superior method introduced by Crockford

In the functional inheritance pattern, Crockford introduces a new superior method via:

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

Where method is :

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

Example:

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;
};

My question is Why don't just assign that.get_name to super_get_name ?

"My question is Why don't just assign that.get_name to super_get_name ?"

Because the way the get_name method has its this value set to the that object is by invoking it as:

that.get_name();

When a function is invoked as the method of an object, the object becomes the value of this in that invocation of the function.

If you had done this instead:

var super_get_name = that.get_name;

super_get_name();

Now you're invoking a detached function, so it doesn't know what its this value should be, and so it uses the default, which is usually the window object.


I don't like the solution that crockford shows at all. Typically, in that situation, you'd simply make a new function right there instead of relying on extensions to Object.prototype to do it for you. (Extending Object.prototype is very ugly IMO.)

var coolcat = function (spec) {
    var that = cat(spec),
        _original_get_name = that.get_name,
        super_get_name = function() {
                             return _original_get_name.apply(that, arguments);
                         };

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

Or in modern implementations, you'd use Function.prototype.bind to create a new function with its this value bound to whatever you provided as the first argument to .bind() .

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

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

In the sense of Crockford's Functional Inheritance Pattern it is completely valid to reference the base class method

var super_get_name = that.get_name;

This is how Crockford teaches it in his lecture JavaScript Master Class , see the part on Functional Inheritance.

Later - the method might be overriden by the derived class - you invoke it simply

super_get_name();

Crockford's superior method makes no sense in the Functional Inheritance Pattern.

Because in my opinion this is never needed in a method defined by a producer function. If you use this in your methods you'll run in all sorts of trouble because of dynamic scoping and manipulation of this :

function mammal(spec){
    var that = {};
    that.legs = Math.round(Math.random() * 4);
    that.get_name = function(){
        return spec.name + "with" + that.legs; // always use that not this
    };
    that.isProperThis = function(){
        console.log( this === that );
    };
    return that;
};
var myMammal = mammal({name: 'Herb'});
myMammal.isProperThis(); // true
myMammal.isProperThis.call(window); // false
setTimeout(myMammal.isProperThis, 1); // false

If you insist on using this in your methods you can no longer treat them as "first-class" variables in JavaScript. Instead you have to convert them to "binders" by calling bind as described in the first answer in this post.

superior is a method defined in the prototype of the Object constructor function. It caches an object's method, so that it returns the original method even if it were changed later.

From JavaScript: The Good Parts, p.54:

The function will invoke the original method even if the property is changed.

The accepted answer given by cookie monster is correct, but I would like to add clarification.

As cookie monster says, if you write

var super_get_name = that.get_name;

invocation of super_get_name will no longer bind any value to this , making it an unbound function.

However, in the case of the example Crockford gives in The Good Parts , it would not matter if super_get_name were unbound by writing it in the way you propose, because this would never be used in its invocation.

You would essentially end up doing the following:

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

(where the right operand executes in the context of the function assigned to cat ).

I assume that Crockford avoids unbound functions here to demonstrate a general principle, even though his specific example does not require binding.

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