Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 645
Salah Eddine Taouririt Avatar asked Jan 12 '23 09:01

Salah Eddine Taouririt


2 Answers

"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;
};
like image 182
cookie monster Avatar answered Jan 13 '23 23:01

cookie monster


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.

like image 42
Semmel Avatar answered Jan 13 '23 22:01

Semmel