基本上有一个很好的优雅机制来模拟 super
,其语法与以下之一一样简单
this.$super.prop()
-
this.$super .prop.apply(this,arguments);
要坚持的标准是:
-
this.$super
必须是对原型的引用。即,如果我在运行时更改超级原型,则此更改将得到反映。这基本上意味着父级有一个新属性,那么应该在运行时通过 super 显示在所有子级上,就像对父级的硬编码引用会反映
- this.$super 的 更改一样.f.apply(this,arguments); 必须适用于递归调用。对于任何在沿着继承链向上进行多个 super 调用的继承链集,您一定不会遇到递归问题。
- 您不得对子级中的超级对象的引用进行硬编码。即
Base.prototype.f.apply(this,arguments);
打败了这一点。
- 您不得使用 X to JavaScript 编译器或 JavaScript 预处理器。
- 必须符合 ES5
简单的实现会是这样的。
var injectSuper = function (parent, child) {
child.prototype.$super = parent.prototype;
};
但这打破了条件2。
迄今为止我见过的最优雅的机制是 IvoWetzel 的 eval< /code> hack
,这几乎是一个 JavaScript 预处理器,因此不符合标准 4。
Basically is there a good elegant mechanism to emulate super
with syntax that is as simple as one of the following
this.$super.prop()
this.$super.prop.apply(this, arguments);
Criteria to uphold are :
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
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.
- You must not hardcode references to super objects in your children. I.e.
Base.prototype.f.apply(this, arguments);
defeats the point.
- You must not use a X to JavaScript compiler or JavaScript preprocessor.
- Must be ES5 compliant
The naive implementation would be something like this.
var injectSuper = function (parent, child) {
child.prototype.$super = parent.prototype;
};
But this breaks condition 2.
The most elegant mechanism I've seen to date is IvoWetzel's eval
hack, which is pretty much a JavaScript preprocessor and thus fails criteria 4.
发布评论
评论(11)
我认为没有一种“免费”的方法可以解决你提到的“递归超级”问题。
我们不能搞乱
this
,因为这样做会迫使我们以非标准的方式改变原型,或者将我们提升到原型链上,从而丢失实例变量。因此,当我们执行超级操作时,必须知道“当前类”和“超类”,而不将该责任传递给this
或其属性之一。我们可以尝试做很多事情,但我认为会产生一些不良后果:
调用 super 方法时添加额外信息
这迫使我们复制当前的类名(这样我们就可以在某些超级字典中查找它的超类),但至少它不像必须复制超类名那么糟糕(因为如果比与类自己的名称的内部耦合更糟糕)
虽然这远非理想,但至少有一些来自 2.x 的历史先例Python。 (他们“修复”了 3.0 的 super,因此它不再需要参数,但我不确定其中涉及多少魔力以及它对 JS 的可移植性)
编辑:工作 小提琴
I don't think there is a "free" way out of the "recursive super" problem you mention.
We can't mess with the
this
because 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 tothis
or one of its properties.There are many some things we could try doing but all I can think have some undesireable consequences:
Add extra info when calling the super method
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)
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)
Edit: Working fiddle
John Resig 发布了一个具有简单但出色的
super
支持的 ineherence 机制。唯一的区别是
super
指向您调用它的基本方法。请查看http://ejohn.org/blog/simple-javascript-inheritance/。
John Resig posted an ineherence mechanism with simple but great
super
support.The only difference is that
super
points to the base method from where you are calling it.Take a look at http://ejohn.org/blog/simple-javascript-inheritance/.
请注意,对于以下实现,当您位于通过 $super 调用的方法内部时,在父类中工作时对属性的访问永远不会解析为子类的方法或变量,除非您访问直接存储在对象本身上的成员(而不是附加到原型上)。这避免了一系列的混乱(读作微妙的错误)。
更新:
这是一个无需
__proto__
即可工作的实现。问题是使用$super
与父对象拥有的属性数量成线性关系。以下实现适用于支持
__proto__
属性的浏览器:示例:
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).Update:
Here is an implementation that works without
__proto__
. The catch is that using$super
is linear in the number of properties the parent object has.The following implementation is for browsers that support the
__proto__
property:Example:
super
的主要困难在于,您需要找到我在此处
所说的内容:包含进行 super 引用的方法的对象。这对于获得正确的语义是绝对必要的。显然,拥有here
的原型也同样好,但这并没有多大区别。以下是静态解决方案:The main difficulty with
super
is that you need to find what I callhere
: the object that contains the method that makes the super reference. That is absolutely necessary to get the semantics right. Obviously, having the prototype ofhere
is just as good, but that doesn’t make much of a difference. The following is a static solution:JsFiddle:
这有什么问题吗?
用法:
使用特权方法而不是公共方法的示例用法。
JsFiddle:
What is wrong with this?
Usage:
Example usage with privileged methods instead of public methods.
本着完整性的精神(也感谢大家的这个帖子,它是一个很好的参考点!)我想加入这个实现。
如果我们承认没有很好的方法来满足上述所有标准,那么我认为这是 Salsify 团队的勇敢努力(我刚刚找到它)在这里找到。这是我见过的唯一实现,既避免了递归问题,又让
.super
成为对正确原型的引用,而无需预编译。因此,我们不是打破标准 1,而是打破标准 5。
该技术取决于使用
Function.caller
(不符合 es5,尽管它在浏览器中得到广泛支持,并且 es6 消除了未来的需求),但它给出了非常优雅的解决所有其他问题(我认为)。.caller
让我们获取方法引用,从而让我们定位原型链中的位置,并使用getter
返回正确的原型。它并不完美,但它与我在这个空间中看到的解决方案有很大不同,您也可以调整它以返回正确的方法(如原始帖子中所示),或者您可以消除用名称注释每个方法的需要,通过将名称传递给超级函数(而不是使用 getter)
这里是一个演示该技术的工作小提琴: jsfiddle< /a>
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
.super
be a reference to the correct prototype, without pre-compilation.So instead of breaking criteria 1, we break 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)..caller
lets us get the method reference which lets us locate where we are in the prototype chain, and uses agetter
to return the correct prototype. Its not perfect but it is widely different solution than what I've seen in this spaceyou 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)
here is a working fiddle demonstrating the technique: jsfiddle
看看 Classy 库;它使用
this.$super
提供类和继承以及对重写方法的访问Have a look at the Classy library; it provides classes and inheritance and access to an overridden method using
this.$super
对于那些不理解OP提出的递归问题的人,这里有一个例子:
原因:Javascript中的
this
是动态绑定的。For those who do not understand the recursion problem the OP presents, here is an example:
The reason:
this
in Javascript is dynamically bound.我想出了一种方法,允许您通过更改执行上下文来使用伪关键字 Super(我还没有在这里看到这种方法。)我发现我根本不满意的缺点是它不能将“Super”变量添加到方法的执行上下文中,而是将其替换为整个执行上下文,这意味着用该方法定义的任何私有方法都将变得不可用...
此方法与“eval hack”非常相似OP 提出,但它不对函数的源字符串,只需在当前执行上下文中使用 eval 重新声明该函数。让它变得更好一点,因为这两种方法都有相同的上述缺点。
非常简单的方法:
一个制作类的例子
前面提到的缺点的一个例子。
另请注意,此方法并不是“超级”子构造函数,这意味着 Super 对其不可用。我只是想解释这个概念,而不是创建一个工作库:)
另外,刚刚意识到我满足了OP的所有条件! (虽然确实应该有一个关于执行上下文的条件)
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...
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.
Very simple method:
An example of making a class
An example of the drawback mentioned earlier.
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 :)
Also, just realized that I met all of OP's conditions! (Although there really should be a condition about execution context)
这是我的版本:lowclass
这是测试中的
super
意大利面汤示例.js 文件(编辑:制作成运行示例):尝试无效的成员访问或无效使用
_super
将引发错误。关于要求:
this.$super 必须是对原型的引用。即,如果我在运行时更改超级原型,则此更改将得到反映。这基本上意味着父级有一个新属性,那么这应该在运行时通过 super 显示在所有子级上,就像对父级的硬编码引用会反映更改一样
不,
_super
帮助器不返回原型,仅返回带有复制描述符的对象,以避免修改受保护和私有原型。此外,从中复制描述符的原型保存在Class
/subclass
调用的范围内。拥有这个就太好了。 FWIW,本机类的行为相同。this.$super.f.apply(this, 参数);必须适用于递归调用。对于任何在沿着继承链向上进行多个 super 调用的继承链集,您一定不会遇到递归问题。
是的,没问题。
您不得对子级中的超级对象进行硬编码引用。即 Base.prototype.f.apply(this,arguments);失败了。
是的
您不得使用 X to JavaScript 编译器或 JavaScript 预处理器。
是的,所有运行时
必须兼容 ES5
是的,它包括一个基于 Babel 的构建步骤(某些类使用 WeakMap,它被编译为非泄漏 ES5 形式)。我不认为这违反了要求 4,它只是允许我编写 ES6+,但它应该仍然可以在 ES5 中工作。诚然,我没有在 ES5 中对此进行太多测试,但如果您想尝试一下,我们绝对可以解决我端的任何构建问题,并且从您端来看,您应该能够在没有任何构建步骤的情况下使用它.
唯一未满足的要求是 1. 那就太好了。但更换原型也许是不好的做法。但实际上,我确实有一些用途,我想交换原型以实现元东西。 “如果能在本机
super
中拥有此功能(这是静态的 :( ),那就太好了,更不用说在此实现中了。为了仔细检查要求 2,我将基本的递归测试添加到了我的 test.js 中,哪个有效(编辑:制作成运行示例):
(对于这些小类来说,类头有点长。我有几个想法可以减少,如果不是所有的助手都需要预先)
Here's my version: lowclass
And here's the
super
spaghetti soup example from the test.js file (EDIT: made into running example):Trying invalid member access or invalid use of
_super
will throw an error.About the requirements:
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
_super
helper 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 theClass
/subclass
call. It would be neat to have this. FWIW, nativeclass
es behave the same.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.
You must not hardcode references to super objects in your children. I.e. Base.prototype.f.apply(this, arguments); defeats the point.
yep
You must not use a X to JavaScript compiler or JavaScript preprocessor.
yep, all runtime
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.
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.To double check requirement 2, I added the basic recursive test to my test.js, which works (EDIT: made into running example):
(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)
我想我有一个更简单的方法......
I think I have a more simple way....