ES6 类多重继承
IT技术
javascript
ecmascript-6
2021-02-02 21:01:20
6个回答
检查下面的示例,super
方法按预期工作。使用一些技巧甚至instanceof
有效(大部分时间):
// base class
class A {
foo() {
console.log(`from A -> inside instance of A: ${this instanceof A}`);
}
}
// B mixin, will need a wrapper over it to be used
const B = (B) => class extends B {
foo() {
if (super.foo) super.foo(); // mixins don't know who is super, guard against not having the method
console.log(`from B -> inside instance of B: ${this instanceof B}`);
}
};
// C mixin, will need a wrapper over it to be used
const C = (C) => class extends C {
foo() {
if (super.foo) super.foo(); // mixins don't know who is super, guard against not having the method
console.log(`from C -> inside instance of C: ${this instanceof C}`);
}
};
// D class, extends A, B and C, preserving composition and super method
class D extends C(B(A)) {
foo() {
super.foo();
console.log(`from D -> inside instance of D: ${this instanceof D}`);
}
}
// E class, extends A and C
class E extends C(A) {
foo() {
super.foo();
console.log(`from E -> inside instance of E: ${this instanceof E}`);
}
}
// F class, extends B only
class F extends B(Object) {
foo() {
super.foo();
console.log(`from F -> inside instance of F: ${this instanceof F}`);
}
}
// G class, C wrap to be used with new decorator, pretty format
class G extends C(Object) {}
const inst1 = new D(),
inst2 = new E(),
inst3 = new F(),
inst4 = new G(),
inst5 = new (B(Object)); // instance only B, ugly format
console.log(`Test D: extends A, B, C -> outside instance of D: ${inst1 instanceof D}`);
inst1.foo();
console.log('-');
console.log(`Test E: extends A, C -> outside instance of E: ${inst2 instanceof E}`);
inst2.foo();
console.log('-');
console.log(`Test F: extends B -> outside instance of F: ${inst3 instanceof F}`);
inst3.foo();
console.log('-');
console.log(`Test G: wraper to use C alone with "new" decorator, pretty format -> outside instance of G: ${inst4 instanceof G}`);
inst4.foo();
console.log('-');
console.log(`Test B alone, ugly format "new (B(Object))" -> outside instance of B: ${inst5 instanceof B}, this one fails`);
inst5.foo();
会打印出来
测试 D:扩展 A、B、C -> D 的外部实例:真 从 A -> A 的内部实例:true 从 B -> B 的内部实例:true 从 C -> C 的实例内部:true 从 D -> D 的内部实例:true —— 测试 E:扩展 A、C -> E 的外部实例:真 从 A -> A 的内部实例:true 从 C -> C 的实例内部:true 从 E -> E 的内部实例:true —— 测试 F:扩展 B -> F 的外部实例:真 从 B -> B 的内部实例:true 从 F -> F 的实例内部:true —— 测试 G:包装器单独使用 C 和“新”装饰器,漂亮的格式 -> G 的外部实例:true 从 C -> C 的实例内部:true —— 单独测试 B,丑陋的格式 "new (B(Object))" -> B 的外部实例:false,这个失败 从 B -> B 的内部实例:true
一个对象只能有一个原型。从两个类继承可以通过创建一个父对象作为两个父原型的组合来完成。
子类化的语法使得可以在声明中做到这一点,因为extends
子句的右侧可以是任何表达式。因此,您可以编写一个根据您喜欢的任何标准组合原型的函数,并在类声明中调用该函数。
Sergio Carneiro 和 Jon 的实现要求您为除一个类之外的所有类定义初始化函数。这是聚合函数的修改版本,它在构造函数中使用默认参数。还包括我的一些评论。
var aggregation = (baseClass, ...mixins) => {
class base extends baseClass {
constructor (...args) {
super(...args);
mixins.forEach((mixin) => {
copyProps(this,(new mixin));
});
}
}
let copyProps = (target, source) => { // this function copies all properties and symbols, filtering out some special ones
Object.getOwnPropertyNames(source)
.concat(Object.getOwnPropertySymbols(source))
.forEach((prop) => {
if (!prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop));
})
}
mixins.forEach((mixin) => { // outside contructor() to allow aggregation(A,B,C).staticFunction() to be called etc.
copyProps(base.prototype, mixin.prototype);
copyProps(base, mixin);
});
return base;
}
这是一个小演示:
class Person{
constructor(n){
this.name=n;
}
}
class Male{
constructor(s='male'){
this.sex=s;
}
}
class Child{
constructor(a=12){
this.age=a;
}
tellAge(){console.log(this.name+' is '+this.age+' years old.');}
}
class Boy extends aggregation(Person,Male,Child){}
var m = new Boy('Mike');
m.tellAge(); // Mike is 12 years old.
此聚合函数将优先选择稍后出现在类列表中的类的属性和方法。
Justin Fagnani描述了一种非常干净(恕我直言)将多个类组合成一个的方法,因为在 ES2015 中,可以使用类表达式创建类。
表达式与声明
基本上,就像您可以使用表达式创建函数一样:
function myFunction() {} // function declaration
var myFunction = function(){} // function expression
你可以对类做同样的事情:
class MyClass {} // class declaration
var MyClass = class {} // class expression
表达式在运行时计算,当代码执行时,而声明是预先执行的。
使用类表达式创建 mixin
您可以使用它来创建一个仅在调用函数时动态创建类的函数:
function createClassExtending(superclass) {
return class AwesomeClass extends superclass {
// you class body here as usual
}
}
很酷的一点是,您可以预先定义整个类,并且只在调用函数时决定它应该扩展哪个类:
class A {}
class B {}
var ExtendingA = createClassExtending(A)
var ExtendingB = createClassExtending(B)
如果你想将多个类混合在一起,因为 ES6 类只支持单继承,你需要创建一个包含所有你想混合在一起的类的类链。因此,假设您想创建一个扩展 A 和 B 的类 C,您可以这样做:
class A {}
class B extends A {}
class C extends B {} // C extends both A and B
问题在于它非常静态。如果您后来决定创建一个扩展 B 但不扩展 A 的 D 类,那么您就有问题了。
但是通过使用类可以是表达式这一事实的一些巧妙技巧,您可以通过不直接将 A 和 B 创建为类,而是将其创建为类工厂(为简洁起见使用箭头函数)来解决这个问题:
class Base {} // some base class to keep the arrow functions simple
var A = (superclass) => class A extends superclass
var B = (superclass) => class B extends superclass
var C = B(A(Base))
var D = B(Base)
请注意我们如何仅在最后一刻决定将哪些类包含在层次结构中。
我的答案似乎更少的代码,它对我有用:
class Nose {
constructor() {
this.booger = 'ready';
}
pick() {
console.log('pick your nose')
}
}
class Ear {
constructor() {
this.wax = 'ready';
}
dig() {
console.log('dig in your ear')
}
}
class Gross extends Classes([Nose,Ear]) {
constructor() {
super();
this.gross = true;
}
}
function Classes(bases) {
class Bases {
constructor() {
bases.forEach(base => Object.assign(this, new base()));
}
}
bases.forEach(base => {
Object.getOwnPropertyNames(base.prototype)
.filter(prop => prop != 'constructor')
.forEach(prop => Bases.prototype[prop] = base.prototype[prop])
})
return Bases;
}
// test it
var grossMan = new Gross();
grossMan.pick(); // eww
grossMan.dig(); // yuck!