在 javascript 中模拟 super

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8032566/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:28:07  来源:igfitidea点击:

Emulate super in javascript

javascriptoopsuper

提问by Raynos

Basically is there a good elegant mechanism to emulate superwith syntax that is as simple as one of the following

基本上有一个很好的优雅机制来模拟super语法,它就像以下之一一样简单

  • this.$super.prop()
  • this.$super.prop.apply(this, arguments);
  • this.$super.prop()
  • this.$super.prop.apply(this, arguments);

Criteria to uphold are :

坚持的标准是:

  1. this.$supermust be a reference to the prototype. i.e. if I change the super prototype at run-time this change will be reflected. This basically means it the parent has a new property then this should be shown at run-time on all children through superjust like a hard coded reference to the parent would reflect changes
  2. this.$super.f.apply(this, arguments);must work for recursive calls. For any chained set of inheritance where multiple super calls are made as you go up the inheritance chain, you must not hit the recursive problem.
  3. You must not hardcode references to super objects in your children. I.e. Base.prototype.f.apply(this, arguments);defeats the point.
  4. You must not use a X to JavaScript compiler or JavaScript preprocessor.
  5. Must be ES5 compliant
  1. this.$super必须是对原型的引用。即如果我在运行时更改超级原型,此更改将被反映。这基本上意味着它的父级有一个新的属性,那么这应该在运行时显示在所有子级上,super就像对父级的硬编码引用将反映更改一样
  2. this.$super.f.apply(this, arguments);必须适用于递归调用。对于任何在继承链上进行多次 super 调用的链式继承,您一定不能遇到递归问题。
  3. 您不得对子项中的超级对象的引用进行硬编码。即Base.prototype.f.apply(this, arguments);败点。
  4. 您不得使用 X 到 JavaScript 编译器或 JavaScript 预处理器。
  5. 必须符合 ES5

The naive implementation would be something like this.

天真的实现将是这样的。

var injectSuper = function (parent, child) {
  child.prototype.$super = parent.prototype;
};

But this breaks condition 2.

但这打破了条件 2

The most elegant mechanism I've seen to date is IvoWetzel's evalhack, which is pretty much a JavaScript preprocessor and thus fails criteria 4.

迄今为止我见过的最优雅的机制是IvoWetzel 的evalhack,它几乎是一个 JavaScript 预处理器,因此不符合标准 4。

采纳答案by hugomg

I don't think there is a "free" way out of the "recursive super" problem you mention.

我认为您提到的“递归超级”问题没有“免费”的方法。

We can't mess with the thisbecause doing so would either force us to change prototypes in a nonstandard way, or move us up the proto chain, losing instance variables. Therefore the "current class" and "super class" must be known when we do the super-ing, without passing that responsibility to thisor one of its properties.

我们不能弄乱 ,this因为这样做会迫使我们以非标准方式更改原型,或者将我们向上移动到原型链,从而丢失实例变量。因此,当我们执行 super-ing 时,必须知道“当前类”和“超类”,而不将该责任传递给this其或其属性之一。

There are many some things we could try doing but all I can think have some undesireable consequences:

我们可以尝试做很多事情,但我认为所有这些都会产生一些不良后果:

  • Add super info to the functions at creation time, access it using arguments.calee or similar evilness.
  • Add extra info when calling the super method

    $super(CurrentClass).method.call(this, 1,2,3)
    

    This forces us to duplicate the current class name (so we can look up its superclass in some super dictionary) but at least it isn't as bad as having to duplicate the superclass name, (since coupling against the inheritance relationships if worse then the inner coupling with a class' own name)

    //Normal Javascript needs the superclass name
    SuperClass.prototype.method.call(this, 1,2,3);
    

    While this is far from ideal, there is at least some historical precedent from 2.x Python. (They "fixed" super for 3.0 so it doesn't require arguments anymore, but I am not sure how much magic that involved and how portable it would be to JS)

  • 在创建时向函数添加超级信息,使用 arguments.calee 或类似的邪恶来访问它。
  • 调用 super 方法时添加额外信息

    $super(CurrentClass).method.call(this, 1,2,3)
    

    这迫使我们复制当前的类名(所以我们可以在某个超级字典中查找它的超类),但至少它不像必须复制超类名称那么糟糕(因为如果比继承关系更糟,那么耦合与继承关系与类自己的名称的内部耦合)

    //Normal Javascript needs the superclass name
    SuperClass.prototype.method.call(this, 1,2,3);
    

    虽然这远非理想,但至少有一些来自2.x Python 的历史先例。(他们“修复”了 3.0 的 super,因此它不再需要参数,但我不确定这涉及多少魔法以及它对 JS 的便携性如何)



Edit: Working fiddle

编辑:工作小提琴

var superPairs = [];
// An association list of baseClass -> parentClass

var injectSuper = function (parent, child) {
    superPairs.push({
        parent: parent,
        child: child
    });
};

function $super(baseClass, obj){
    for(var i=0; i < superPairs.length; i++){
        var p = superPairs[i];
        if(p.child === baseClass){
            return p.parent;
        }
    }
}

回答by ngryman

John Resig posted an ineherence mechanism with simple but great supersupport. The only difference is that superpoints to the base method from where you are calling it.

John Resig 发布了一个具有简单但强大super支持的内在机制。唯一的区别是super指向您调用它的基本方法。

Take a look at http://ejohn.org/blog/simple-javascript-inheritance/.

看看http://ejohn.org/blog/simple-javascript-inheritance/

回答by Thomas Eding

Note that for the following implementation, when you are inside a method that is invoked via $super, access to properties while working in the parent class never resolve to the child class's methods or variables, unless you access a member that is stored directly on the object itself (as opposed to attached to the prototype). This avoids a slew of confusion (read as subtle bugs).

请注意,对于以下实现,当您在通过 调用的方法中$super时,在父类中工作时对属性的访问永远不会解析为子类的方法或变量,除非您访问直接存储在对象本身上的成员(相对于附加到原型)。这避免了一系列混乱(读作微妙的错误)。

Update: Here is an implementation that works without __proto__. The catch is that using $superis linear in the number of properties the parent object has.

更新:这是一个无需__proto__. 问题在于 using$super与父对象具有的属性数量呈线性关系。

function extend (Child, prototype, /*optional*/Parent) {
    if (!Parent) {
        Parent = Object;
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    for (var x in prototype) {
        if (prototype.hasOwnProperty(x)) {
            Child.prototype[x] = prototype[x];
        }
    }
    Child.prototype.$super = function (propName) {
        var prop = Parent.prototype[propName];
        if (typeof prop !== "function") {
            return prop;
        }
        var self = this;
        return function () {
            var selfPrototype = self.constructor.prototype;
            var pp = Parent.prototype;
            for (var x in pp) {
                self[x] = pp[x];
            }
            try {
                return prop.apply(self, arguments);
            }
            finally {
                for (var x in selfPrototype) {
                    self[x] = selfPrototype[x];
                }
            }
        };
    };
}

The following implementation is for browsers that support the __proto__property:

以下实现适用于支持该__proto__属性的浏览器:

function extend (Child, prototype, /*optional*/Parent) {
    if (!Parent) {
        Parent = Object;
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    for (var x in prototype) {
        if (prototype.hasOwnProperty(x)) {
            Child.prototype[x] = prototype[x];
        }
    }
    Child.prototype.$super = function (propName) {
        var prop = Parent.prototype[propName];
        if (typeof prop !== "function") {
            return prop;
        }
        var self = this;
        return function (/*arg1, arg2, ...*/) {
            var selfProto = self.__proto__;
            self.__proto__ = Parent.prototype;
            try {
                return prop.apply(self, arguments);
            }
            finally {
                self.__proto__ = selfProto;
            }
        };
    };
}

Example:

例子:

function A () {}
extend(A, {
    foo: function () {
        return "A1";
    }
});

function B () {}
extend(B, {
    foo: function () {
        return this.$super("foo")() + "_B1";
    }
}, A);

function C () {}
extend(C, {
    foo: function () {
        return this.$super("foo")() + "_C1";
    }
}, B);


var c = new C();
var res1 = c.foo();
B.prototype.foo = function () {
    return this.$super("foo")() + "_B2";
};
var res2 = c.foo();

alert(res1 + "\n" + res2);

回答by Axel Rauschmayer

The main difficulty with superis that you need to find what I call here: the object that contains the method that makes the super reference. That is absolutely necessary to get the semantics right. Obviously, having the prototype of hereis just as good, but that doesn't make much of a difference. The following is a static solution:

主要的困难super是你需要找到我所说的here:包含产生超级引用的方法的对象。这对于获得正确的语义是绝对必要的。显然,拥有 的原型here同样好,但这并没有太大区别。以下是静态解决方案:

// Simulated static super references (as proposed by Allen Wirfs-Brock)
// http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super

//------------------ Library

function addSuperReferencesTo(obj) {
    Object.getOwnPropertyNames(obj).forEach(function(key) {
        var value = obj[key];
        if (typeof value === "function" && value.name === "me") {
            value.super = Object.getPrototypeOf(obj);
        }
    });
}

function copyOwnFrom(target, source) {
    Object.getOwnPropertyNames(source).forEach(function(propName) {
        Object.defineProperty(target, propName,
            Object.getOwnPropertyDescriptor(source, propName));
    });
    return target;
};

function extends(subC, superC) {
    var subProto = Object.create(superC.prototype);
    // At the very least, we keep the "constructor" property
    // At most, we preserve additions that have already been made
    copyOwnFrom(subProto, subC.prototype);
    addSuperReferencesTo(subProto);
    subC.prototype = subProto;
};

//------------------ Example

function A(name) {
    this.name = name;
}
A.prototype.method = function () {
    return "A:"+this.name;
}

function B(name) {
    A.call(this, name);
}
// A named function expression allows a function to refer to itself
B.prototype.method = function me() {
    return "B"+me.super.method.call(this);
}
extends(B, A);

var b = new B("hello");
console.log(b.method()); // BA:hello

回答by Bill Barry

JsFiddle:

JsFiddle

What is wrong with this?

这有什么问题?

'use strict';

function Class() {}
Class.extend = function (constructor, definition) {
    var key, hasOwn = {}.hasOwnProperty, proto = this.prototype, temp, Extended;

    if (typeof constructor !== 'function') {
        temp = constructor;
        constructor = definition || function () {};
        definition = temp;
    }
    definition = definition || {};

    Extended = constructor;
    Extended.prototype = new this();

    for (key in definition) {
        if (hasOwn.call(definition, key)) {
            Extended.prototype[key] = definition[key];
        }
    }

    Extended.prototype.constructor = Extended;

    for (key in this) {
        if (hasOwn.call(this, key)) {
            Extended[key] = this[key];
        }
    }

    Extended.$super = proto;
    return Extended;
};

Usage:

用法:

var A = Class.extend(function A () {}, {
    foo: function (n) { return n;}
});
var B = A.extend(function B () {}, {
    foo: function (n) {
        if (n > 100) return -1;
        return B.$super.foo.call(this, n+1);
    }
});
var C = B.extend(function C () {}, {
    foo: function (n) {
        return C.$super.foo.call(this, n+2);
    }
});

var c = new C();
document.write(c.foo(0) + '<br>'); //3
A.prototype.foo = function(n) { return -n; };
document.write(c.foo(0)); //-3

Example usage with privileged methods instead of public methods.

使用特权方法而不是公共方法的示例用法。

var A2 = Class.extend(function A2 () {
    this.foo = function (n) {
        return n;
    };
});
var B2 = A2.extend(function B2 () {
    B2.$super.constructor();
    this.foo = function (n) {
        if (n > 100) return -1;
        return B2.$super.foo.call(this, n+1);
    };
});
var C2 = B2.extend(function C2 () {
    C2.$super.constructor();
    this.foo = function (n) {
        return C2.$super.foo.call(this, n+2);
    };
});

//you must remember to constructor chain
//if you don't then C2.$super.foo === A2.prototype.foo

var c = new C2();
document.write(c.foo(0) + '<br>'); //3

回答by monastic-panic

In the spirit of completeness (also thank you everyone for this thread it has been an excellent point of reference!) I wanted to toss in this implementation.

本着完整性的精神(也感谢大家提供这个线程,它一直是一个很好的参考点!)我想在这个实现中折腾。

If we are admitting that there is no good way of meeting all of the above criteria, then I think this is a valiant effort by the Salsify team (I just found it) found here. This is the only implementation I've seen that avoids the recursion problem but also lets .superbe a reference to the correct prototype, without pre-compilation.

如果我们承认没有满足上述所有标准的好方法,那么我认为这是在这里找到的 Salsify 团队(我刚刚找到它)的英勇努力。这是我见过的唯一避免递归问题的实现,但也.super可以引用正确的原型,而无需预编译。

So instead of breaking criteria 1, we break 5.

因此,我们没有打破标准 1,而是打破 5。

the technique hinges on using Function.caller(not es5 compliant, though it is extensively supported in browsers and es6 removes future need), but it gives really elegant solution to all the other issues (I think). .callerlets us get the method reference which lets us locate where we are in the prototype chain, and uses a getterto return the correct prototype. Its not perfect but it is widely different solution than what I've seen in this space

该技术取决于使用Function.caller(不符合 es5,尽管它在浏览器中得到广泛支持并且 es6 消除了未来的需求),但它为所有其他问题提供了非常优雅的解决方案(我认为)。.caller让我们获得方法引用,它让我们定位我们在原型链中的位置,并使用 agetter返回正确的原型。它并不完美,但它与我在这个领域看到的解决方案大不相同

var Base = function() {};

Base.extend = function(props) {
  var parent = this, Subclass = function(){ parent.apply(this, arguments) };

    Subclass.prototype = Object.create(parent.prototype);

    for(var k in props) {
        if( props.hasOwnProperty(k) ){
            Subclass.prototype[k] = props[k]
            if(typeof props[k] === 'function')
                Subclass.prototype[k]._name = k
        }
    }

    for(var k in parent) 
        if( parent.hasOwnProperty(k)) Subclass[k] = parent[k]        

    Subclass.prototype.constructor = Subclass
    return Subclass;
};

Object.defineProperty(Base.prototype, "super", {
  get: function get() {
    var impl = get.caller,
        name = impl._name,
        foundImpl = this[name] === impl,
        proto = this;

    while (proto = Object.getPrototypeOf(proto)) {
      if (!proto[name]) break;
      else if (proto[name] === impl) foundImpl = true;
      else if (foundImpl)            return proto;
    }

    if (!foundImpl) throw "`super` may not be called outside a method implementation";
  }
});

var Parent = Base.extend({
  greet: function(x) {
    return x + " 2";
  }
})

var Child = Parent.extend({
  greet: function(x) {
    return this.super.greet.call(this, x + " 1" );
  }
});

var c = new Child
c.greet('start ') // => 'start 1 2'

you can also adjust this to return the correct method (as in the original post) or you can remove the need to annotate each method with the name, by passing in the name to a super function (instead of using a getter)

您还可以调整它以返回正确的方法(如原始帖子中所示),或者您可以通过将名称传递给超级函数(而不是使用 getter)来消除使用名称注释每个方法的需要

here is a working fiddle demonstrating the technique: jsfiddle

这是一个演示该技术的工作小提琴:jsfiddle

回答by BAM5

I came up with a way that will allow you to use a pseudo keyword Super by changing the execution context (A way I have yet to see be presented on here.) The drawback that I found that I'm not happy with at all is that it cannot add the "Super" variable to the method's execution context, but instead replaces it the entire execution context, this means that any private methods defined with the method become unavailable...

我想出了一种方法,允许您通过更改执行上下文来使用伪关键字 Super(一种我还没有看到的方法)。我发现我根本不满意的缺点是它不能将“Super”变量添加到方法的执行上下文中,而是将其替换为整个执行上下文,这意味着使用该方法定义的任何私有方法都变得不可用......

This method is very similar to the "eval hack" OP presented however it doesn't do any processing on the function's source string, just redeclares the function using eval in the current execution context. Making it a bit better as both of the methods have the same aforementioned drawback.

此方法与提供的“eval hack”OP 非常相似,但是它不对函数的源字符串进行任何处理,只是在当前执行上下文中使用 eval 重新声明函数。由于这两种方法都具有上述相同的缺点,因此使它变得更好一些。

Very simple method:

非常简单的方法:

function extend(child, parent){

    var superify = function(/* Super */){
        // Make MakeClass scope unavailable.
        var child = undefined,
            parent = undefined,
            superify = null,
            parentSuper = undefined,
            oldProto = undefined,
            keys = undefined,
            i = undefined,
            len = undefined;

        // Make Super available to returned func.
        var Super = arguments[0];
        return function(/* func */){
            /* This redefines the function with the current execution context.
             * Meaning that when the returned function is called it will have all of the current scopes variables available to it, which right here is just "Super"
             * This has the unfortunate side effect of ripping the old execution context away from the method meaning that no private methods that may have been defined in the original scope are available to it.
             */
            return eval("("+ arguments[0] +")");
        };
    };

    var parentSuper = superify(parent.prototype);

    var oldProto = child.prototype;
    var keys = Object.getOwnPropertyNames(oldProto);
    child.prototype = Object.create(parent.prototype);
    Object.defineProperty(child.prototype, "constructor", {enumerable: false, value: child});

    for(var i = 0, len = keys.length; i<len; i++)
        if("function" === typeof oldProto[keys[i]])
            child.prototype[keys[i]] = parentSuper(oldProto[keys[i]]);
}

An example of making a class

一个创建类的例子

function P(){}
P.prototype.logSomething = function(){console.log("Bro.");};

function C(){}
C.prototype.logSomething = function(){console.log("Cool story"); Super.logSomething.call(this);}

extend(C, P);

var test = new C();
test.logSomething(); // "Cool story" "Bro."

An example of the drawback mentioned earlier.

前面提到的缺点的一个例子。

(function(){
    function privateMethod(){console.log("In a private method");}

    function P(){};

    window.C = function C(){};
    C.prototype.privilagedMethod = function(){
        // This throws an error because when we call extend on this class this function gets redefined in a new scope where privateMethod is not available.
        privateMethod();
    }

    extend(C, P);
})()

var test = new C();
test.privilagedMethod(); // throws error

Also note that this method isn't "superifying" the child constructor meaning that Super isn't available to it. I just wanted to explain the concept, not make a working library :)

另请注意,此方法不会“超化”子构造函数,这意味着 Super 对它不可用。我只是想解释这个概念,而不是制作一个可用的库:)

Also, just realized that I met all of OP's conditions! (Although there really should be a condition about execution context)

另外,刚刚意识到我满足了 OP 的所有条件!(虽然确实应该有一个关于执行上下文的条件)

回答by trusktr

Here's my version: lowclass

这是我的版本:lowclass

And here's the superspaghetti soup example from the test.js file (EDIT: made into running example):

super是 test.js 文件中的意大利面汤示例(编辑:制作为运行示例):

var SomeClass = Class((public, protected, private) => ({

    // default access is public, like C++ structs
    publicMethod() {
        console.log('base class publicMethod')
        protected(this).protectedMethod()
    },

    checkPrivateProp() {
        console.assert( private(this).lorem === 'foo' )
    },

    protected: {
        protectedMethod() {
            console.log('base class protectedMethod:', private(this).lorem)
            private(this).lorem = 'foo'
        },
    },

    private: {
        lorem: 'blah',
    },
}))

var SubClass = SomeClass.subclass((public, protected, private, _super) => ({

    publicMethod() {
        _super(this).publicMethod()
        console.log('extended a public method')
        private(this).lorem = 'baaaaz'
        this.checkPrivateProp()
    },

    checkPrivateProp() {
        _super(this).checkPrivateProp()
        console.assert( private(this).lorem === 'baaaaz' )
    },

    protected: {

        protectedMethod() {
            _super(this).protectedMethod()
            console.log('extended a protected method')
        },

    },

    private: {
        lorem: 'bar',
    },
}))

var GrandChildClass = SubClass.subclass((public, protected, private, _super) => ({

    test() {
        private(this).begin()
    },

    reallyBegin() {
        protected(this).reallyReallyBegin()
    },

    protected: {
        reallyReallyBegin() {
            _super(public(this)).publicMethod()
        },
    },

    private: {
        begin() {
            public(this).reallyBegin()
        },
    },
}))

var o = new GrandChildClass
o.test()

console.assert( typeof o.test === 'function' )
console.assert( o.reallyReallyBegin === undefined )
console.assert( o.begin === undefined )
<script> var module = { exports: {} } </script>
<script src="https://unpkg.com/[email protected]/index.js"></script>
<script> var Class = module.exports // get the export </script>

Trying invalid member access or invalid use of _superwill throw an error.

尝试无效的成员访问或无效使用_super将引发错误。

About the requirements:

关于要求:

  1. this.$super must be a reference to the prototype. i.e. if I change the super prototype at run-time this change will be reflected. This basically means it the parent has a new property then this should be shown at run-time on all children through super just like a hard coded reference to the parent would reflect changes

    No, the _superhelper doesn't return the prototype, only an object with copied descriptors to avoid modification of the protected and private prototypes. Furthermore, the prototype from which the descriptors are copied from is held in the scope of the Class/subclasscall. It would be neat to have this. FWIW, native classes behave the same.

  2. this.$super.f.apply(this, arguments); must work for recursive calls. For any chained set of inheritance where multiple super calls are made as you go up the inheritance chain, you must not hit the recursive problem.

    yep, no problem.

  3. You must not hardcode references to super objects in your children. I.e. Base.prototype.f.apply(this, arguments); defeats the point.

    yep

  4. You must not use a X to JavaScript compiler or JavaScript preprocessor.

    yep, all runtime

  5. Must be ES5 compliant

    Yes, it includes a Babel-based build step (f.e. lowclass uses WeakMap, which is compiled to a non-leaky ES5 form). I don't think this defeats requirement 4, it just allows me to write ES6+ but it should still work in ES5. Admittedly I haven't done much testing of this in ES5, but if you'd like to try it, we can definitely iron out any builds issues on my end, and from your end you should be able to consume it without any build steps.

  1. this.$super 必须是对原型的引用。即如果我在运行时更改超级原型,此更改将被反映。这基本上意味着它的父有一个新的属性,那么这应该在运行时通过 super 在所有子级上显示,就像对父级的硬编码引用将反映更改

    不,_super助手不返回原型,只返回一个带有复制描述符的对象,以避免修改受保护和私有原型。此外,从中复制描述符的原型保存在Class/subclass调用的范围内。有这个就好了。FWIW,本机classes 的行为相同。

  2. this.$super.f.apply(this, arguments); 必须适用于递归调用。对于任何在继承链上进行多次 super 调用的链式继承,您一定不能遇到递归问题。

    是的,没问题。

  3. 您不得对子项中的超级对象的引用进行硬编码。即 Base.prototype.f.apply(this, arguments); 击败了这一点。

    是的

  4. 您不得使用 X 到 JavaScript 编译器或 JavaScript 预处理器。

    是的,所有运行时

  5. 必须符合 ES5

    是的,它包括一个基于 Babel 的构建步骤(fe lowclass 使用 Wea​​kMap,它被编译为非泄漏的 ES5 形式)。我不认为这违背了要求 4,它只是允许我编写 ES6+,但它仍然可以在 ES5 中工作。诚然,我没有在 ES5 中对此进行过多测试,但是如果您想尝试一下,我们绝对可以解决我这边的任何构建问题,从您的角度来看,您应该能够在没有任何构建步骤的情况下使用它.

The only requirement not met is 1. It would be nice. But maybe it is bad practice to be swapping out prototypes. But actually, I do have uses where I would like to swap out prototypes in order to achieve meta stuff. 'Twould be nice to have this feature with native super(which is static :( ), let alone in this implementation.

唯一不满足的要求是 1。这会很好。但也许更换原型是不好的做法。但实际上,我确实有一些用途,我想更换原型以实现元数据。'在本机super(它是静态的 :( )中使用此功能会很好,更不用说在此实现中了。

To double check requirement 2, I added the basic recursive test to my test.js, which works (EDIT: made into running example):

为了仔细检查要求 2,我将基本递归测试添加到我的 test.js 中,该测试有效(编辑:制作为运行示例):

const A = Class((public, protected, private) => ({
    foo: function (n) { return n }
}))

const B = A.subclass((public, protected, private, _super) => ({
    foo: function (n) {
        if (n > 100) return -1;
        return _super(this).foo(n+1);
    }
}))

const C = B.subclass((public, protected, private, _super) => ({
    foo: function (n) {
        return _super(this).foo(n+2);
    }
}))

var c = new C();
console.log( c.foo(0) === 3 )
<script> var module = { exports: {} } </script>
<script src="https://unpkg.com/[email protected]/index.js"></script>
<script> var Class = module.exports // get the export </script>

(the class header is a bit long for these little classes. I have a couple ideas to make it possible to reduce that if not all the helpers are needed up front)

(这些小类的类头有点长。我有一些想法可以减少这种情况,如果不是预先需要所有帮助程序)

回答by ThiefMaster

Have a look at the Classylibrary; it provides classes and inheritance and access to an overridden method using this.$super

看看Classy库;它提供类和继承,并使用this.$super

回答by Thomas Eding

For those who do not understand the recursion problem the OP presents, here is an example:

对于那些不理解 OP 提出的递归问题的人,这里有一个例子:

function A () {}
A.prototype.foo = function (n) {
    return n;
};

function B () {}
B.prototype = new A();
B.prototype.constructor = B;
B.prototype.$super = A.prototype;
B.prototype.foo = function (n) {
    if (n > 100) return -1;
    return this.$super.foo.call(this, n+1);
};

function C () {}
C.prototype = new B();
C.prototype.constructor = C;
C.prototype.$super = B.prototype;
C.prototype.foo = function (n) {
    return this.$super.foo.call(this, n+2);
};


alert(new C().foo(0)); // alerts -1, not 3

The reason: thisin Javascript is dynamically bound.

原因:this在Javascript中是动态绑定的。