ES6类多重继承

BTC*_*BTC 117 javascript ecmascript-6

我在BabelJSMDN(完全没有信息)上完成了我的大部分研究,但请随时告诉我,如果我没有仔细查看有关ES6规格的更多信息.

我想知道ES6是否支持多种继承,就像其他鸭式语言一样.例如,我可以这样做:

class Example extends ClassOne, ClassTwo {
    constructor() {
    }
}
Run Code Online (Sandbox Code Playgroud)

将多个类扩展到新类?如果是这样,解释器是否会更喜欢ClassTne上的ClassTne方法/属性?

Poe*_*rin 68

检查我的示例下方,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();
Run Code Online (Sandbox Code Playgroud)

将打印出来

Test D: extends A, B, C -> outside instance of D: true
from A -> inside instance of A: true
from B -> inside instance of B: true
from C -> inside instance of C: true
from D -> inside instance of D: true
-
Test E: extends A, C -> outside instance of E: true
from A -> inside instance of A: true
from C -> inside instance of C: true
from E -> inside instance of E: true
-
Test F: extends B -> outside instance of F: true
from B -> inside instance of B: true
from F -> inside instance of F: true
-
Test G: wraper to use C alone with "new" decorator, pretty format -> outside instance of G: true
from C -> inside instance of C: true
-
Test B alone, ugly format "new (B(Object))" -> outside instance of B: false, this one fails
from B -> inside instance of B: true

链接到小提琴


Poi*_*nty 63

一个对象只能有一个原型.可以通过将父对象创建为两个父原型的组合来继承两个类.

子类化的语法使得在声明中可以这样做,因为extends子句的右侧可以是任何表达式.因此,您可以编写一个根据您喜欢的标准组合原型的函数,并在类声明中调用该函数.

  • @qwertymk牢记`__proto__`本身是已弃用的功能。它反映了内部原型链接,但实际上不是内部原型链接。 (2认同)
  • 这是我想出的解决方案:https://esdiscuss.org/topic/symbol-for-modifying-property-lookup。范例:`class Foo扩展了新的MultiClass(Bar,Baz,One,Two){...}`。传递给`new MultiClass`的最后一个构造方法的方法和属性具有最高优先级,它们只是混入了新的原型中。我认为,如果使用ES6 Proxies重新实现,则存在更好的解决方案,但是还没有足够的本机支持。 (2认同)

Cho*_*ang 19

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;
}
Run Code Online (Sandbox Code Playgroud)

这是一个小小的演示:

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.
Run Code Online (Sandbox Code Playgroud)

此聚合函数将更喜欢稍后在类列表中出现的类的属性和方法.

  • 当我尝试使用这个反应`组件`时,它不起作用.只是对于其他可能为此目的而想要它的人. (2认同)
  • `bind|call|apply|toString` 不会是自己的属性名称。从原型中省略“name|length”会导致相当多的误报。当“prop”是一个符号时,“prop.match()”将不起作用!请不要盲目抄袭。 (2认同)

Sti*_*itt 12

Justin Fagnani 描述了一种非常干净(imho)的方法,使用在ES2015中可以使用类表达式创建类的事实将多个类组合成一个.

表达式与声明

基本上,就像你可以用表达式创建一个函数:

function myFunction() {}      // function declaration
var myFunction = function(){} // function expression
Run Code Online (Sandbox Code Playgroud)

你可以用类做同样的事情:

class MyClass {}             // class declaration
var MyClass = class {}       // class expression
Run Code Online (Sandbox Code Playgroud)

表达式在运行时在代码执行时进行计算,而声明则在事先执行.

使用类表达式创建mixins

您可以使用它来创建一个仅在调用函数时动态创建类的函数:

function createClassExtending(superclass) {
  return class AwesomeClass extends superclass {
    // you class body here as usual
  }
}
Run Code Online (Sandbox Code Playgroud)

关于它的一个很酷的事情是你可以预先定义整个类,并且只在你调用函数时决定它应该扩展到哪个类:

class A {}
class B {}
var ExtendingA = createClassExtending(A)
var ExtendingB = createClassExtending(B)
Run Code Online (Sandbox Code Playgroud)

如果要将多个类混合在一起,因为ES6类仅支持单继承,您需要创建一个包含要混合在一起的所有类的类链.所以假设你想要创建一个扩展A和B的类C,你可以这样做:

class A {}
class B extends A {}
class C extends B {}  // C extends both A and B
Run Code Online (Sandbox Code Playgroud)

这个问题是它非常静态.如果你以后决定要制作一个扩展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)
Run Code Online (Sandbox Code Playgroud)

请注意我们如何仅在最后时刻决定要在层次结构中包含哪些类.

帮助我们建立未来!

当然,如果你像我一样,这会激发你在Javascript中构建用于多重继承的终极库.如果您愿意,请帮助我做到这一点!看看这个项目,如果可以,请帮忙!

话筒

mics(发音:mix)是一个在Javascript中进行多重继承的库.受到Justin Fagnani的优秀博客文章"Real"Mixins和Javascript Classes的启发,麦克风试图围绕使用类表达式(工厂)作为mixins的概念构建一个最小的库.mics扩展了博客文章中提出的概念,通过使mixins成为一流的公民,可以直接用于实例化对象,并且可以与其他mixin混合使用,而不仅仅是与类混合.


tod*_*dmo 11

我的答案似乎更少的代码,它对我有用:

    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!
Run Code Online (Sandbox Code Playgroud)

  • 加一创意! (9认同)
  • 多么有趣的例子 lmao (4认同)

qwe*_*ymk 8

对于原型继承的工作方式,这实际上是不可能的.让我们来看看继承的道具如何在js中工作

var parent = {a: function() { console.log('ay'); }};
var child = Object.create(parent);
child.a() // first look in child instance, nope let's go to it's prototype
          // then look in parent, found! return the method
Run Code Online (Sandbox Code Playgroud)

让我们看看当你访问一个不存在的道具时会发生什么:

child.b; // first look in child instance, nope let's go to it's prototype
         // then look in parent, nope let's go to it's prototype
         // then look in Object.prototype, nope let's go to it's prototype
         // then look at null, give up and return undefined
Run Code Online (Sandbox Code Playgroud)

你可以使用mixins来获得一些功能,但你不会得到后期绑定:

var a = {x: '1'};
var b = {y: '2'};
var c = createWithMixin([a, b]);
c.x; // 1
c.y; // 2
b.z = 3;
c.z; // undefined
Run Code Online (Sandbox Code Playgroud)

VS

var a = {x: 1}
var o = Object.create(a);
o.x; // 1
a.y = 2;
o.y; // 2
Run Code Online (Sandbox Code Playgroud)


ken*_*ley 5

https://www.npmjs.com/package/ts-mixer

拥有最好的 TS 支持和许多其他有用的功能!