Search code examples
javascriptinheritancemootools

How do I call a parent method with a different name than the one I am in?


I want to call [Parent].f() from within [Child].g() rather than [Child].f().

Suppose I have the MooTools code:

var Parent = new Class({
    f: function () {
        console.log('Parent.f()');
    }
});

var Child = new Class({
    Extends: Parent,

    f: function () {
        console.log('Child.f()');
    },

    g: function () {
        // Call Parent.f()
        this.parent.f();
    }
});

var o = new Child();

o.g();

Obviously this.parent.f() is nonsense because MooTools makes this.parent to be the parent class's method with the same name as the method you are in, i.e. in Child.g() this would be Parent.g().

So, how can I call P.f() ?


Solution

  • The problem you have is that Child hasOwnProperty f, so calling child.f() will get it on the instance and not the prototype.

    Rather than hack with switching this.$caller.$name as suggested in a different answer, just call it from the parent prototype direct:

    this.$constructor.parent.prototype.f.call(this);
    // or
    this.$constructor.parent.prototype.f.apply(this, arguments);
    

    Of course, if you don't care about Reflection-ish, you can just do P.prototype.f.call(this) - but it is non-DRY as it means the method needs to know the name of the super.

    The problem with this approach is if it will work if Parent is extending another parent and so on but it should be fine for most cases. It will also fail if Child has no parent or no f, where it should logically revert to running its own implementation of f.

    var Parent = new Class({
        f: function(msg){
            console.log('Parent.f() says ' + msg);
        }
    });
    
    var Child = new Class({
        Extends: Parent,
        f: function(){
            console.log('Child.f()');
        },   
        g: function(){
            // Call Parent.f()
            this.$constructor.parent.prototype.f.apply(this, arguments);
        }
    });
    
    new Child().g('hello');
    

    If you need to do this often, you can create a new method on your parent classes as a mixin. eg.

    Class.parenting = new Class({
        // mixin class that allows call
        _parent: function(method, args){
            this.$constructor.parent.prototype[method].apply(this, args);    
        }.protect() // private, restricted to methods
    });
    
    var Parent = new Class({
        Implements: [Class.parenting],
        f: function(msg){
            console.log('Parent.f() says ' + msg);
        }
    });
    
    var Child = new Class({
        Extends: Parent,
        f: function(){
            console.log('Child.f()');
        },   
        g: function(){
            this._parent('f', arguments);
        }
    });
    
    var c = new Child();
    c.g('hello');
    c._parent('f'); // throws, only instances methods can call this