ES6类多重inheritance
我已经在BabelJS和MDN (根本没有任何信息)上完成了大部分的研究工作,但请随时告诉我,如果我没有足够小心地查找关于ES6 Spec的更多信息。
我想知道ES6是否支持与其他鸭式语言一样的多重inheritance。 例如,我可以做些什么:
class Example extends ClassOne, ClassTwo { constructor() { } }
将多个class级扩展到新class级? 如果是的话,解释器会比ClassOne更喜欢ClassTwo的方法/属性吗?
一个对象只能有一个原型。 从两个类inheritance可以通过创build一个父对象作为两个父原型的组合来完成。
子类的语法可以在声明中做到这一点,因为extends
子句的右边可以是任何expression式。 因此,您可以编写一个函数,根据您喜欢的任何标准组合原型,并在类声明中调用该函数。
检查我的示例波纹pipe, super
方法按预期工作。 使用一些技巧甚至instanceof
(大部分时间):
//基类 A级{ foo(){ console.log(`从A - >内的实例A:$ {this instanceof A}`); } } // B mixin,需要一个包装器才能使用 const B =(B)=> class extends B { foo(){ if(super.foo)super.foo(); // mixins不知道谁超级,防不了方法 console.log(`从B - > B的实例:$ {this instanceof B}`); } }; // C mixin,需要一个包装器才能使用 const C =(C)=> class extends C { foo(){ if(super.foo)super.foo(); // mixins不知道谁超级,防不了方法 console.log(`来自C - > C:$ {this instanceof C}`); } }; // D类,扩展了A,B和C,保留了组合和超级方法 D类扩展C(B(A)){ foo(){ super.foo(); console.log(`从D - > D:$ {this instanceof D}`); } } // E类,扩展A和C E类延伸C(A){ foo(){ super.foo(); console.log(`从E - > E:$ {this instanceof E}`的实例``); } } // F类,只扩展B. F类扩展B(Object){ foo(){ super.foo(); console.log(`从F - > F:$ {this instanceof F}`的内部实例`); } } / / G类,C包装用于新的装饰,漂亮的格式 G类扩展C(Object){} const inst1 = new D(), inst2 = new E(), inst3 = new F(), inst4 = new G(), inst5 = new(B(Object)); //实例只有B,丑陋的格式 console.log(`Test D:extends A,B,C - > D:$ {inst1 instanceof D}`的外部实例); inst1.foo(); 的console.log( ' - '); console.log(`Test E:extends A,C - > E:$ {inst2 instanceof E}的外部实例`); inst2.foo(); 的console.log( ' - '); console.log(`Test F:extends B - > F:$ {inst3 instanceof F}的外部实例`); inst3.foo(); 的console.log( ' - '); console.log(`testingG:包装使用C单独与“新”装饰,漂亮的格式 - > G的外部实例:$ {inst4 instanceof G}`); inst4.foo(); 的console.log( ' - '); console.log(`单独testingB,丑陋格式“new(B(Object))” - > B的外部实例:$ {inst5 instanceof B},这个失败`); inst5.foo();
将打印出来
testingD:扩展A,B,C - > D的外部实例:true 从A - >里面的实例A:true 从B - > B的实例中:true 从C - >里面的实例C:true 从D - >里面的实例D:true - testingE:扩展A,C - > E的外部实例:true 从A - >里面的实例A:true 从C - > C的内部实例:true 从E - >里面的E实例:true - testingF:扩展B - > F的外部实例:true 从B - > B的实例中:true 从F - > F的内部实例:true - testingG:使用“新”装饰器,单独使用C,相当格式 - > G:true的外部实例 从C - >里面的实例C:true - 单独testingB,丑陋的格式为“new(B(Object))” - > B的外部实例为false,这个失败 从B - > B的实例中:true
链接到小提琴
原型inheritance的工作原理并不可行。 让我们看看inheritance的道具如何在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
让我们来看看当你访问一个不存在的道具时会发生什么:
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
你可以使用mixin来获得一些function,但是你不会迟到的绑定:
var a = {x: '1'}; var b = {y: '2'}; var c = createWithMixin([a, b]); cx; // 1 cy; // 2 bz = 3; cz; // undefined
VS
var a = {x: 1} var o = Object.create(a); ox; // 1 ay = 2; oy; // 2
我想出了这些解决scheme:
'use strict'; const _ = require( 'lodash' ); module.exports = function( ParentClass ) { if( ! ParentClass ) ParentClass = class {}; class AbstractClass extends ParentClass { /** * Constructor **/ constructor( configs, ...args ) { if ( new.target === AbstractClass ) throw new TypeError( "Cannot construct Abstract instances directly" ); super( args ); if( this.defaults === undefined ) throw new TypeError( new.target.name + " must contain 'defaults' getter" ); this.configs = configs; } /** * Getters / Setters **/ // Getting module configs get configs() { return this._configs; } // Setting module configs set configs( configs ) { if( ! this._configs ) this._configs = _.defaultsDeep( configs, this.defaults ); } } return AbstractClass; }
用法:
const EventEmitter = require( 'events' ); const AbstractClass = require( './abstracts/class' )( EventEmitter ); class MyClass extends AbstractClass { get defaults() { return { works: true, minuses: [ 'u can have only 1 class as parent wich was\'t made by u', 'every othere classes should be your\'s' ] }; } }
只要你用自定义的书写类来制作这些技巧,它就可以被链接在一起。 但是我们很快就想扩展一些不是这样写的function/类,否则你将没有机会继续循环。
const EventEmitter = require( 'events' ); const A = require( './abstracts/a' )(EventEmitter); const B = require( './abstracts/b' )(A); const C = require( './abstracts/b' )(B);
在v5.4.1中使用–harmony标志为我工作
从页面es6-features.org/#ClassInheritanceFromExpressions中 ,可以编写一个聚合函数来实现多重inheritance:
类Rectangle扩展聚合(Shape,Colored,ZCoord){}
var aggregation = (baseClass, ...mixins) => { let base = class _Combined extends baseClass { constructor (...args) { super(...args) mixins.forEach((mixin) => { mixin.prototype.initializer.call(this) }) } } let copyProps = (target, source) => { Object.getOwnPropertyNames(source) .concat(Object.getOwnPropertySymbols(source)) .forEach((prop) => { if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) return Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop)) }) } mixins.forEach((mixin) => { copyProps(base.prototype, mixin.prototype) copyProps(base, mixin) }) return base }
但是这已经在像 聚合 这样的图书馆中提供了 。
塞尔吉奥·卡内罗和乔恩的实现要求你为除一个类之外的所有类定义初始化函数。 这是一个聚合函数的修改版本,它使用构造函数中的默认参数。 包括也是我的一些意见。
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.
这个聚合函数将优先select稍后出现在类列表中的类的属性和方法。
Justin Fagnani 描述了一种非常干净的(imho)方式来组合多个类,使用ES2015中的类可以用类expression式创build。
expression式与声明
基本上,就像你可以用expression式创build一个函数一样:
function myFunction() {} // function declaration var myFunction = function(){} // function expression
你可以用类来做同样的事情:
class MyClass {} // class declaration var MyClass = class {} // class expression
expression式在运行时被计算,当代码执行时,而声明被预先执行。
使用类expression式来创build混合
您可以使用它来创build一个函数,只有在函数被调用时才能dynamic创build一个类:
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类只支持单一的inheritance,你需要创build一个包含你想要混合的所有类的类链。 假设你想创build一个扩展A和B的类C,你可以这样做:
class A {} class B extends A {} class C extends B {} // C extends both A and B
这个问题是非常静态的。 如果您以后决定要创build一个扩展B而不是A的D类,那么您有一个问题。
但是有一些聪明的诡计使用类可以是expression式的事实,可以通过创buildA和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)
请注意,我们只是在最后时刻决定将哪些类包含在层次结构中。
帮助我们build设未来!
当然,如果你像我一样,这启发你build立在Javascript中的多重inheritance的终极库。 如果你做到了,请帮助我做到这一点! 看看这个项目,如果可以的话,帮忙!
话筒
mics (发音:mix)是一个使Javascript中的多重inheritance轻而易举的库。 受到Justin Fagnani出色的博客文章“Real”Mixins with Javascript Classes的启发,Mics尝试围绕使用类expression式(工厂)作为mixin的概念构build一个最小的库。 mics扩展了在博客文章中提出的概念,通过使mixins可以直接用于实例化对象,并且可以与其他mixin混合,而不是与类一起混合。
那么Object.assign给你提供了一些可能性,尽pipe有点像ES6类的组合。
class Animal { constructor(){ Object.assign(this, new Shark()) Object.assign(this, new Clock()) } } class Shark { // only what's in constructor will be on the object, ence the weird this.bite = this.bite. constructor(){ this.color = "black"; this.bite = this.bite } bite(){ console.log("bite") } eat(){ console.log('eat') } } class Clock{ constructor(){ this.tick = this.tick; } tick(){ console.log("tick"); } } let animal = new Animal(); animal.bite(); console.log(animal.color); animal.tick();
我没有看到这个在任何地方使用,但实际上非常有用。 你可以使用function shark(){}
来代替class,但是有一些使用class的好处。
我相信与关键字extend
不同的唯一不同之处在于函数不仅仅生存在prototype
上,而且还生存在对象本身。
因此,现在当你做new Shark()
的shark
创build的shark
有一种bite
方法,而只有它的原型有一种eat
没有简单的方法来做多个类inheritance。 我遵循联想和inheritance的结合来实现这种行为。
class Person { constructor(firstname, lastname, age){ this.firstname = firstname, this.lastname = lastname this.Age = age } fullname(){ return this.firstname +" " + this.lastname; } } class Organization { constructor(orgname){ this.orgname = orgname; } } class Employee extends Person{ constructor(firstname, lastname, age,id) { super(firstname, lastname, age); this.id = id; } } var emp = new Employee("John", "Doe", 33,12345); Object.assign(emp, new Organization("Innovate")); console.log(emp.id); console.log(emp.orgname); console.log(emp.fullname());
希望这是有帮助的。
使用Mixins for ES6多inheritance。
let classTwo = Base => class extends Base{ // ClassTwo Code }; class Example extends classTwo(ClassOne) { constructor() { } }
使用自定义函数的范围来处理与es6的多重inheritance
var aggregation = (baseClass, ...mixins) => { let base = class _Combined extends baseClass { constructor (...args) { super(...args) mixins.forEach((mixin) => { mixin.prototype.initializer.call(this) }) } } let copyProps = (target, source) => { Object.getOwnPropertyNames(source) .concat(Object.getOwnPropertySymbols(source)) .forEach((prop) => { if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) return Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop)) }) } mixins.forEach((mixin) => { copyProps(base.prototype, mixin.prototype) copyProps(base, mixin) }) return base } class Colored { initializer () { this._color = "white" } get color () { return this._color } set color (v) { this._color = v } } class ZCoord { initializer () { this._z = 0 } get z () { return this._z } set z (v) { this._z = v } } class Shape { constructor (x, y) { this._x = x; this._y = y } get x () { return this._x } set x (v) { this._x = v } get y () { return this._y } set y (v) { this._y = v } } class Rectangle extends aggregation(Shape, Colored, ZCoord) {} var rect = new Rectangle(7, 42) rect.z = 1000 rect.color = "red" console.log(rect.x, rect.y, rect.z, rect.color)
这是一个真棒/非常糟糕的方式来扩展多个类。 我正在利用Babel把这些function放到我的代码中。 该函数创build一个inheritanceclass1的新类,class1inheritanceclass2,等等。 它有它的问题,但一个有趣的想法。
var _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (obj) { return typeof obj } : function (obj) { return obj && typeof Symbol === 'function' && obj.constructor === Symbol ? 'symbol' : typeof obj } function _inherits (subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + ( typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass))) } subClass.prototype = Object.create( superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }) if (superClass) { Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass.__proto__ // eslint-disable-line no-proto } } function _m (...classes) { let NewSuperClass = function () {} let c1 = NewSuperClass for (let c of classes) { _inherits(c1, c) c1 = c } return NewSuperClass } import React from 'react' /** * Adds `this.log()` to your component. * Log message will be prefixed with the name of the component and the time of the message. */ export default class LoggingComponent extends React.Component { log (...msgs) { if (__DEBUG__) { console.log(`[${(new Date()).toLocaleTimeString()}] [${this.constructor.name}]`, ...msgs) } } } export class MyBaseComponent extends _m(LoggingComponent, StupidComponent) {}