JavaScript inheritance and super constructor

2020-02-06 18:23发布

问题:

I have found and adapted a JavaScript "class" extend function from coffeescript:

var extend = (function() {

    var hasProp = Object.prototype.hasOwnProperty;

    function ctor(child) {
        this.constructor = child;
    }

    return function(child, parent) {
        for (var key in parent) {
            if (hasProp.call(parent, key)) {
                child[key] = parent[key];
            }
        }
        ctor.prototype = parent.prototype;
        child.prototype = new ctor(child);
        child.__super__ = parent.prototype;
        // child.prototype.__super__ = parent.prototype; // better?
        return child;
    };

})();

I am wondering, if there is a reason why they used child.__super__ instead of child.prototype.__super__ (see out-commented code line).

I like the out-commented version more because:

  1. You can access super properties via this.__super__.propertyName instead of ClassName.__super__.propertyName. So you have no redundancy in the class naming.

  2. This makes even more sense for nested inheritance since you can use this.__super__.__super__.propertyName instead of ClassName.__super__.constructor.__super__.propertyName

I do not see any reason for it, but you could even still call "static" functions in a "static" way like that:

ClassName.prototype.__super__.constructor.staticMethod()

Are there any drawbacks with my version that I might have overlooked?

EDIT: I corrected the line to var hasProp = Object.prototype.hasOwnProperty;

回答1:

Because you're not supposed to use __super__ in your code at all.

It's a compiler artifact, every use of the super macro/keyword/whatever it is will compile to

ClassName.__super__.methodName.call(this, …) // or
ClassName.__super__.methodName.apply(this, …)
// or, in static class functions even
ClassName.__super___.constructor.functionName.call(this, …)

They don't trust the dynamic this binding that you have proposed to use (this.__super__), they rather went for a static reference of the parent. Actually it might have been a good idea not to use a property at all, but just a local super variable in their module scope.


Also, this.__super__ will not work in an inherited method:

function A() { }
A.prototype.method = function() { console.log("works") };

function B() { A.call(this); }
B.prototype = Object.create(A.prototype);
B.prototype.__super__ = A.prototype;
B.prototype.method = function() { this.__super__.method.call(this); }


function C() { B.call(this); }
C.prototype = Object.create(B.prototype);
C.prototype.__super__ = B.prototype;

var b = new B(), c = new C();
b.method() // "works"
c.method() // Maximum recursion depth exceeded

Stack Overflow because you did not get the .__super__ that you expected!