ES6 Class 多重继承
ES6 Class Multiple inheritance
我已经在 BabelJS and on MDN 上完成了我的大部分研究(根本没有任何信息),但如果我没有足够仔细地四处寻找更多信息,请随时告诉我关于 ES6 规范
我想知道 ES6 是否以与其他鸭子类型语言相同的方式支持多重继承。例如,我可以做类似的事情吗:
class Example extends ClassOne, ClassTwo {
constructor() {
}
}
将多个 class 扩展到新的 class?如果是这样,口译员会更喜欢 ClassTwo 的 methods/properties 而不是 ClassOne 吗?
这对于原型继承的工作方式来说是不可能的。让我们看看继承的 props 在 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
您可以使用 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
对
var a = {x: 1}
var o = Object.create(a);
o.x; // 1
a.y = 2;
o.y; // 2
一个对象只能有一个原型。从两个 classes 继承可以通过创建一个父对象作为两个父原型的组合来完成。
subclassing 的语法使得在声明中这样做成为可能,因为 extends
子句的右侧可以是任何表达式。因此,您可以编写一个根据您喜欢的任何标准组合原型的函数,并在 class 声明中调用该函数。
我想出了这些解决方案:
'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/class - 你将没有机会继续循环。
const EventEmitter = require( 'events' );
const A = require( './abstracts/a' )(EventEmitter);
const B = require( './abstracts/b' )(A);
const C = require( './abstracts/b' )(B);
在带有 --harmony 标志的节点 v5.4.1 中为我工作
检查下面的示例,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();
会打印出来
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
这是扩展多个 classes 的 awesome/really 蹩脚方法。我正在使用 Babel 放入我的转译代码中的几个函数。该函数创建一个新的 class 继承 class1,class1 继承 class2,依此类推。它有它的问题,但是一个有趣的想法。
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) {}
使用 Mixins 实现 ES6 多重继承。
let classTwo = Base => class extends Base{
// ClassTwo Code
};
class Example extends classTwo(ClassOne) {
constructor() {
}
}
从页面es6-features.org/#ClassInheritanceFromExpressions,可以编写一个允许多重继承的聚合函数:
class Rectangle extends aggregation(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
}
但这已经在像 aggregation.
这样的库中提供了
使用 extent 和自定义函数来处理 es6 的多重继承
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)
好吧 Object.assign 让你有可能做一些接近的事情,尽管有点像用 ES6 classes 组合。
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
有一个 bite
方法,而只有它的原型有一个 eat
方法
Sergio Carneiro's and Jon's implementation 要求您为除一个 class 之外的所有对象定义初始化函数。这是聚合函数的修改版本,它在构造函数中使用默认参数。其中也包括一些我的评论。
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.
此聚合函数将优先选择 class 中稍后出现在 class 列表中的属性和方法。
没有简单的方法来进行多重 class 继承。我遵循关联和继承的组合来实现这种行为。
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());
希望这对您有所帮助。
Justin Fagnani describes 一种非常干净(恕我直言)的方式将多个 classes 组合成一个,因为在 ES2015 中,classes 可以用 [=46 创建=] 表达式.
表达式与声明
基本上,就像您可以使用表达式创建函数一样:
function myFunction() {} // function declaration
var myFunction = function(){} // function expression
你可以用 classes 做同样的事情:
class MyClass {} // class declaration
var MyClass = class {} // class expression
表达式是在运行时计算的,当代码执行时,而声明是事先执行的。
使用 class 表达式创建 mixin
您可以使用它来创建一个仅在调用该函数时才动态创建 class 的函数:
function createClassExtending(superclass) {
return class AwesomeClass extends superclass {
// you class body here as usual
}
}
它最酷的地方在于,您可以预先定义整个 class,并且只在您调用函数时决定应该扩展哪个 class:
class A {}
class B {}
var ExtendingA = createClassExtending(A)
var ExtendingB = createClassExtending(B)
如果你想将多个classes混合在一起,因为ES6 classes只支持单继承,你需要创建一个包含所有classes的链=46=]es 你想混合在一起。假设您想创建一个扩展 A 和 B 的 class C,您可以这样做:
class A {}
class B extends A {}
class C extends B {} // C extends both A and B
问题在于它非常静态。如果您后来决定要制作一个扩展 B 但不扩展 A 的 class D,那您就有问题了。
但是使用 classes 可以是表达式这一事实的一些聪明的技巧,你可以通过创建 A 和 B 来解决这个问题,而不是直接作为 classes,而是作为 class工厂(为简洁起见使用箭头函数):
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 包含在层次结构中的。
我根据这些原则制作了一个库,您可以看看:mics
这个 ES6 解决方案对我有用:
多个-inheritance.js
export function allOf(BaseClass, ...Mixins) {
function copyProperties(target, source) {
const allPropertyNames = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source))
allPropertyNames.forEach((propertyName) => {
if (propertyName.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
return
Object.defineProperty(target, propertyName, Object.getOwnPropertyDescriptor(source, propertyName))
})
}
class Base extends BaseClass
{
constructor (...args) {
super(...args)
Mixins.forEach((Mixin) => {
copyProperties(this, new Mixin(...args))
})
}
}
Mixins.forEach((mixin) => {
copyProperties(Base.prototype, Mixin.prototype)
})
return Base
}
main.js
import { allOf } from "./multiple-inheritance.js"
class A
{
constructor(name) {
this.name = name
}
sayA() {
return this.name
}
}
class B
{
constructor(name) {
this.name = name
}
sayB() {
return this.name
}
}
class AB extends allOf(A, B)
{
sayAB() {
return this.name
}
}
const ab = new AB("ab")
console.log("ab.sayA() = "+ab.sayA()+", ab.sayB() = "+ab.sayB()+", ab.sayAB() = "+ab.sayAB())
浏览器控制台的产量:
ab.sayA() = ab, ab.sayB() = ab, ab.sayAB() = ab
我也会添加我的解决方案 - 我发现它对我自己来说是最友好的,从我在这个帖子中读到的内容来看。
export const aggregate = (...mixins) => (Base) => {
const 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, mixin);
copyProps(Base.prototype, mixin.prototype);
});
return Base;
};
你可以像这样使用它:
class _MyBaseClass {}
const MyBaseClass = aggregate(ExtensionOne, ExtensionTwo)(_MyBaseClass);
作为概念验证,我做了以下功能。它需要一个 classes 列表,并将它们组合成一个新的 class(最后一个原型获胜,因此没有冲突)。创建组合函数时,用户可以选择使用所有原始构造函数 [sic!] 或传递自己的构造函数。这是这个实验的最大挑战:提出构造函数应该做什么的描述。将方法复制到原型中不是问题,但新组合对象的预期逻辑是什么。或者它应该是无构造函数的?在 Python 中,据我所知,它找到了 匹配的 构造函数,但 JS 中的函数更容易接受,因此可以将几乎所有内容传递给函数,并从它赢得的签名中传递说清楚。
我认为它没有优化,但目的是探索可能性。 instanceof
不会像预期的那样运行,我想这很糟糕,因为 class-oriented 开发人员喜欢将其用作工具。
也许JavaScript只是没有。
/*
(c) Jon Krazov 2019
Below is an experiment searching boundaries of JavaScript.
It allows to compute one class out of many classes.
Usage 1: Without own constructor
If no constructor is passed then constructor of each class will be called
with params passed in object. In case of missing params, constructor
will be called without params.
Example:
const MyClass1 = computeClass([Class1, Class2, Class3]);
const myClass1Instance = new MyClass1({
'Class1': [1, 2],
'Class2': ['test'],
'Class3': [(value) => value],
});
Usage 2: With own constructor
If constructor is passed in options object (second param) then it will
be called in place of constructors of all classes.
Example:
const MyClass2 = computeClass([Class1, Class2, Class3], {
ownConstructor(param1) {
this.name = param1;
}
});
const myClass2Instance = new MyClass2('Geoffrey');
*/
// actual function
var computeClass = (classes = [], { ownConstructor = null } = {}) => {
const noConstructor = (value) => value != 'constructor';
const ComputedClass = ownConstructor === null
? class ComputedClass {
constructor(args) {
classes.forEach((Current) => {
const params = args[Current.name];
if (params) {
Object.assign(this, new Current(...params));
} else {
Object.assign(this, new Current());
}
})
}
}
: class ComputedClass {
constructor(...args) {
if (typeof ownConstructor != 'function') {
throw Error('ownConstructor has to be a function!');
}
ownConstructor.call(this, ...args);
}
};
const prototype = classes.reduce(
(composedPrototype, currentClass) => {
const partialPrototype = Object.getOwnPropertyNames(currentClass.prototype)
.reduce(
(result, propName) =>
noConstructor(propName)
? Object.assign(
result,
{ [propName]: currentClass.prototype[propName] }
)
: result,
{}
);
return Object.assign(composedPrototype, partialPrototype);
},
{}
);
Object.entries(prototype).forEach(([prop, value]) => {
Object.defineProperty(ComputedClass.prototype, prop, { value });
});
return ComputedClass;
}
// demo part
var A = class A {
constructor(a) {
this.a = a;
}
sayA() { console.log('I am saying A'); }
}
var B = class B {
constructor(b) {
this.b = b;
}
sayB() { console.log('I am saying B'); }
}
console.log('class A', A);
console.log('class B', B);
var C = computeClass([A, B]);
console.log('Composed class');
console.log('var C = computeClass([A, B]);', C);
console.log('C.prototype', C.prototype);
var c = new C({ A: [2], B: [32] });
console.log('var c = new C({ A: [2], B: [32] })', c);
console.log('c instanceof A', c instanceof A);
console.log('c instanceof B', c instanceof B);
console.log('Now c will say:')
c.sayA();
c.sayB();
console.log('---');
var D = computeClass([A, B], {
ownConstructor(c) {
this.c = c;
}
});
console.log(`var D = computeClass([A, B], {
ownConstructor(c) {
this.c = c;
}
});`);
var d = new D(42);
console.log('var d = new D(42)', d);
console.log('Now d will say:')
d.sayA();
d.sayB();
console.log('---');
var E = computeClass();
console.log('var E = computeClass();', E);
var e = new E();
console.log('var e = new E()', e);
最初发布 here(gist.github.com)。
我花了半周的时间自己弄明白了,并写了一整篇文章,https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS,希望对你们中的一些人有所帮助。
简而言之,以下是如何在 JavaScript 中实现 MI:
class Car {
constructor(brand) {
this.carname = brand;
}
show() {
return 'I have a ' + this.carname;
}
}
class Asset {
constructor(price) {
this.price = price;
}
show() {
return 'its estimated price is ' + this.price;
}
}
class Model_i1 { // extends Car and Asset (just a comment for ourselves)
//
constructor(brand, price, usefulness) {
specialize_with(this, new Car(brand));
specialize_with(this, new Asset(price));
this.usefulness = usefulness;
}
show() {
return Car.prototype.show.call(this) + ", " + Asset.prototype.show.call(this) + ", Model_i1";
}
}
mycar = new Model_i1("Ford Mustang", "0K", 16);
document.getElementById("demo").innerHTML = mycar.show();
这里是 specialize_with() 一行:
function specialize_with(o, S) { for (var prop in S) { o[prop] = S[prop]; } }
在javascript中你不能给class(构造函数)2个不同的原型对象并且因为javascript中的继承与原型一起工作所以你不能使用超过1个继承对于一个 class 但 你可以聚合并加入 Prototype 对象的 属性 和 class 中的主要 属性 手动重构parent classes and next extends that new version and join class to your target class 有你的问题的代码:
let Join = (...classList) => {
class AggregatorClass {
constructor() {
classList.forEach((classItem, index) => {
let propNames = Object.getOwnPropertyNames(classItem.prototype);
propNames.forEach(name => {
if (name !== 'constructor') {
AggregatorClass.prototype[name] = classItem.prototype[name];
}
});
});
classList.forEach(constructor => {
Object.assign(AggregatorClass.prototype, new constructor())
});
}
}
return AggregatorClass
};
https://www.npmjs.com/package/ts-mixer
拥有最好的 TS 支持和许多其他有用的功能!
我的答案似乎代码更少而且对我有用:
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!
我一直在使用这样的模式来编写复杂的多继承事物:
var mammal = {
lungCapacity: 200,
breath() {return 'Breathing with ' + this.lungCapacity + ' capacity.'}
}
var dog = {
catchTime: 2,
bark() {return 'woof'},
playCatch() {return 'Catched the ball in ' + this.catchTime + ' seconds!'}
}
var robot = {
beep() {return 'Boop'}
}
var robotDogProto = Object.assign({}, robot, dog, {catchTime: 0.1})
var robotDog = Object.create(robotDogProto)
var livingDogProto = Object.assign({}, mammal, dog)
var livingDog = Object.create(livingDogProto)
此方法使用的代码很少,并允许覆盖默认属性(就像我在 robotDogProto 中使用自定义 catchTime 所做的那样)
下面的解决方案(class 通过复制实例字段和原型属性进行克隆)对我有用。我正在使用普通的 JS(即不是 Typescript),带有 JsDoc 注释和 VSCode 用于编译时类型检查。
求解界面很简单:
class DerivedBase extends cloneClass(Derived, Base) {}
//To add another superclass:
//class Der1Der2Base extends cloneClass(Derived2, DerivedBase)
let o = new DerivedBase()
所涉及的 classes 可以像普通 ES6 classes 一样创建。
但是需要完成以下操作:
- 使用 isInstanceOf() 而不是内置运算符 instanceof。
- 不要使用 'super' 调用基类 class 的非构造函数成员,而是使用函数 super2()。
- 不要在构造函数中编写任何代码,而是将其写入名为 'init()' 的 class 方法中,该方法依次由构造函数调用。请参阅下面的示例。
/* Paste the entire following text into the browser's dev console */
/* Tested on latest version of Chrome, Microsoft Edge and FireFox (Win OS)*/
/* Works with JSDoc in VSCode */
/* Not tested on minified/obfuscated code */
//#region library
const log = console.log
/**
* abbreviation for Object.getPrototypeOf()
* @param {any} o
*/
function proto(o) { return Object.getPrototypeOf(o) }
/** @param {function} fn */
function callIfNonNull(fn) { if (fn != null) { return fn() } }
/**
* @param {boolean} b
* @param {()=>string} [msgFn]
*/
function assert(b, msgFn) {
if (b) { return }
throw new Error('assert failed: ' + ((msgFn == null) ? '' : msgFn()))
}
/** @param {any} o */
function asAny(o) { return o }
/**
* Use this function instead of super.<functionName>
* @param {any} obj
* @param {string} attr the name of the function/getter/setter
* @param {any} cls the class for the current method
* @param {any[]} args arguments to the function/getter/setter
*/
function super2(obj, attr, cls, ...args) {
let nms = clsNms(obj)
assert(nms[0] == nms[1])
const objCls = proto(obj)
const superObj = proto(ancestorNamed(objCls, cls.name))
assert(superObj != obj)
const attrDscr = getOwnOrBasePropDscr(superObj, attr)
if (attrDscr == null) { return null }
let attrVal = attrDscr['value']
const attrGet = attrDscr['get']
const attrSet = attrDscr['set']
if (attrVal == null) {
if (attrGet != null) {
if (attrSet != null) {
assert([0, 1].includes(args.length))
attrVal = ((args.length == 0) ? attrGet : attrSet)
} else {
assert(args.length == 0,
() => 'attr=' + attr + ', args=' + args)
attrVal = attrGet
}
} else if (attrSet != null) {
assert(args.length == 1)
attrVal = attrSet
} else {
assert(false)//no get or set or value!!!!
}
assert(typeof attrVal == 'function')
}
if (typeof attrVal != 'function') { return attrVal }
const boundFn = attrVal.bind(obj)
return boundFn(...args)
}
/**
* Use this function to call own prop instead of overriden prop
* @param {any} obj
* @param {string} attr the name of the function/getter/setter
* @param {any} cls the class for the current method
* @param {any[]} args arguments to the function/getter/setter
*/
function ownProp(obj, attr, cls, ...args) {
let protoObj = ancestorNamed(proto(obj), cls.name)
const attrDscr = Object.getOwnPropertyDescriptor(protoObj, attr)
if (attrDscr == null) {
log(`ownProp(): own property '${attr}' does not exist...`)
return null
}
let attrVal = attrDscr['value']
const attrGet = attrDscr['get']
const attrSet = attrDscr['set']
if (attrVal == null) {
if (attrGet != null) {
if (attrSet != null) {
assert([0, 1].includes(args.length))
attrVal = ((args.length == 0) ? attrGet : attrSet)
} else {
assert(args.length == 0,
() => 'attr=' + attr + ', args=' + args)
attrVal = attrGet
}
} else if (attrSet != null) {
assert(args.length == 1)
attrVal = attrSet
} else {
assert(false)//no get or set or value!!!!
}
assert(typeof attrVal == 'function')
}
if (typeof attrVal != 'function') {
log(`ownProp(): own property '${attr}' not a fn...`)
return attrVal
}
const boundFn = attrVal.bind(obj)
return boundFn(...args)
}
/**
* @param {any} obj
* @param {string} nm
*/
function getOwnOrBasePropDscr(obj, nm) {
let rv = Object.getOwnPropertyDescriptor(obj, nm)
if (rv != null) { return rv }
let protObj = proto(obj)
if (protObj == null) { return null }
return getOwnOrBasePropDscr(protObj, nm)
}
/**
* @param {any} obj
* @param {string} nm
*/
function ancestorNamed(obj, nm) {
const ancs = ancestors(obj)
for (const e of ancs) {
if ((e.name || e.constructor.name) == nm) { return e }
}
}
/**
* @template typeOfDerivedCls
* @template typeOfBaseCls
* @param {typeOfDerivedCls} derivedCls
* @param {typeOfBaseCls} baseCls
* @returns {typeOfDerivedCls & typeOfBaseCls}
*/
function cloneClass(derivedCls, baseCls) {
const derClsNm = derivedCls['name'], baseClsNm = baseCls['name']
const gbl = globalThis
//prevent unwanted cloning and circular inheritance:
if (isInstanceOf(baseCls, asAny(derivedCls))) { return asAny(baseCls) }
if (isInstanceOf(derivedCls, asAny(baseCls))) { return asAny(derivedCls) }
//Object does not derive from anything; it is the other way round:
if (derClsNm == 'Object') { return cloneClass(baseCls, derivedCls) }
//use cached cloned classes if available
if (gbl.clonedClasses == null) { gbl.clonedClasses = {} }
const k = derClsNm + '_' + baseClsNm, kVal = gbl.clonedClasses[k]
if (kVal != null) { return kVal }
//clone the base class of the derived class (cloning occurs only if needed)
let derBase = cloneClass(proto(derivedCls), baseCls)
//clone the derived class
const Clone = class Clone extends derBase {
/** @param {any[]} args */
constructor(...args) {
super(...args)
ownProp(this, 'init', Clone, ...args)
}
}
//clone the properties of the derived class
Object.getOwnPropertyNames(derivedCls['prototype'])
.filter(prop => prop != 'constructor')
.forEach(prop => {
const valToSet =
Object.getOwnPropertyDescriptor(derivedCls['prototype'], prop)
if (typeof valToSet == 'undefined') { return }
Object.defineProperty(Clone.prototype, prop, valToSet)
})
//set the name of the cloned class to the same name as its source class:
Object.defineProperty(Clone, 'name', { value: derClsNm, writable: true })
//cache the cloned class created
gbl.clonedClasses[k] = Clone
log('Created a cloned class with id ' + k + '...')
return asAny(Clone)
}
/**
* don't use instanceof throughout your application, use this fn instead
* @param {any} obj
* @param {Function} cls
*/
function isInstanceOf(obj, cls) {
if (obj instanceof cls) { return true }
return clsNms(obj).includes(cls.name)
}
/** @param {any} obj */
function clsNms(obj) {
return ancestors(obj).map(/** @param {any} e */ e =>
e.name || e.constructor.name)
}
/**
* From: https://gist.github.com/ceving/2fa45caa47858ff7c639147542d71f9f
* Returns the list of ancestor classes.
*
* Example:
* ancestors(HTMLElement).map(e => e.name || e.constructor.name)
* => ["HTMLElement", "Element", "Node", "EventTarget", "Function", "Object"]
* @param {any} anyclass
*/
function ancestors(anyclass) {
if (anyclass == null) { return [] }
return [anyclass, ...(ancestors(proto(anyclass)))]
}
//#endregion library
//#region testData
class Base extends Object {
/** @param {any[]} args */
constructor(...args) {//preferably accept any input
super(...args)
ownProp(this, 'init', Base, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class Base...')
//TODO: add code here to get the args as a named dictionary
//OR, follow a practice of parameterless constructors and
//initial-value-getting methods for class field intialization
/** example data field of the base class */
this.baseClsFld = 'baseClsFldVal'
}
m1() { log('Executed base class method m1') }
b1() { log('Executed base class method b1') }
get baseProp() { return 'basePropVal' }
}
class Derived extends Object {//extend Object to allow use of 'super'
/** @param {any[]} args */
constructor(...args) {//convention: accept any input
super(...args)
ownProp(this, 'init', Derived, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class Derived...')
this.derClsFld = 'derclsFldVal'
}
m1() {
const log = /** @param {any[]} args */(...args) =>
console.log('Derived::m1(): ', ...args)
log(`super['m1']: `, super['m1'])
super2(this, 'm1', Derived)
log(`super['baseProp']`, super['baseProp'])
log(`super2(this, 'baseProp', Derived)`,
super2(this, 'baseProp', Derived))
log(`super2(this, 'nonExistentBaseProp', Derived)`,
super2(this, 'nonExistentBaseProp', Derived))
}
m2() {
log('Executed derived class method 2')
}
}
class DerivedBase extends cloneClass(Derived, Base) {
/** @param {any[]} args */
constructor(...args) {
super(...args)
ownProp(this, 'init', DerivedBase, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class DerivedBase...')
}
}
log('Before defining o (object of DerivedBase)...')
let o = new DerivedBase()
log('After defining o (object of DerivedBase)...')
class Derived2 extends Base {
/** @param {any} args */
constructor(...args) {
//convention/best practice: use passthrough constructors for the classes
//you write
super(...args)
ownProp(this, 'init', Derived2, ...args)
}
/**
* @param {any[]} _args
*/
init(..._args) {
log('Executing init() of class Derived2...')
}
derived2func() { log('Executed Derived2::derived2func()') }
}
class Der1Der2Base extends cloneClass(Derived2, DerivedBase) {
/** @param {any} args */
constructor(...args) {
//convention/best practice: use passthrough constructors for the classes
//you write
super(...args)
ownProp(this, 'init', Der1Der2Base, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing original ctor of class Der1Der2Base...')
}
}
log('Before defining o2...')
const o2 = new Der1Der2Base()
log('After defining o2...')
class NotInInheritanceChain { }
//#endregion testData
log('Testing fields...')
log('o.derClsFld:', o.derClsFld)
log('o.baseClsFld:', o.baseClsFld)
//o.f3 JSDoc gives error in VSCode
log('Test method calls')
o.b1()
o.m1()
o.m2()
//o.m3() //JSDoc gives error in VSCode
log('Test object o2')
o2.b1()
o2.m1()
o2.derived2func()
//don't use instanceof throughout your application, use this fn instead
log('isInstanceOf(o,DerivedBase)', isInstanceOf(o, DerivedBase))
log('isInstanceOf(o,Derived)', isInstanceOf(o, Derived))
log('isInstanceOf(o,Base)', isInstanceOf(o, Base))
log('isInstanceOf(o,NotInInheritanceChain)',
isInstanceOf(o, NotInInheritanceChain))
注意事项:JSDoc 交集运算符 & 可能并不总是有效。在这种情况下,可能需要使用其他一些解决方案。例如,可能需要定义一个单独的接口 class,它将 'manually' 组合 2 个 class。此接口 class 可以从其中一个 class 扩展,另一个 class 的接口可以使用 VsCode 快速修复选项自动实现。
回答
这是不可能的。一个对象在Javascript.
中只能有一个prototype
解决方法
您可以使用 Object.assign
,它可以工作,但不安全并且不提供自动完成或任何类型安全。
class Example {
constructor (props) {
Object.assign(this, new Class1(props))
Object.assign(this, new Class2(props))
}
}
您应该考虑的另一种方式
在这个例子中,我假设扩展 many 的目标是能够在一行中构建 Example
class,然后可以访问所有可能的方法-扩展 classes.
可选:如果方法依赖于超class中的方法,我建议创建另一个BaseClass
并有class Class1 extends BaseClass {}
。这样 Example
的可重用部分就不会被重写。
class Class1 extends BaseClass {}
class Class2 extends BaseClass {}
class Example {
class1: Class1 // Sorry for the Typescript
class2: Class2
constructor (props) {
this.class1 = new Class1(props)
this.class2 = new Class2(props)
}
}
const example = new Example(props)
example.class1.someMethod()
我建议使用组合而不是使用继承,它更灵活,并且您可以获得相同的收益,即在不同的地方重用相同的代码 类。
import Controller from 'your-favorite-framework';
class Example extends ClassTwoMixin(ClassOneMixin(Controller)) {
constructor() {
}
}
const ClassOneMixin = (superclass) => class extends superclass {}
const ClassTwoMixin = (superclass) => class extends superclass {}
要了解更多信息,请搜索:“组合优于继承”
我写作是为了理解和增加 js 编程的灵活性。这段代码是参考上面的代码写的
class A{
constructor(name)
{
this.name=name;
}
getname=function(){return this.name};
}
B=(x)=>(class B extends (()=>x||Object)(){
constructor(surname,name)
{ super(name);
this.surname=surname;
}
getsurname(){return this.surname};
})
class C extends B(A){
constructor(name,surname)
{
super(surname,name);
}
getfullname(){return this.name+" "+this.surname};
};
let person=new C("Ed","Boon");
console.log(person.getname());//output Ed
console.log(person.getsurname());//output Boon
console.log(person.getfullname());//output Ed Boon
console.log(person);
console.log(person.__proto__.constructor); //person.__proto__ properties inherit from C class
console.log(person.__proto__.__proto__.constructor); //person.__proto__.__proto__ properties inherit from B class
console.log(person.__proto__.__proto__.__proto__.constructor); //person.__proto__.__proto__ .__proto__ properties inherit from A class
注意:person instanceof A true
但是 person instanceof B 是假的,因为 B 看起来是函数。
B 只出现为 class,在 运行 中定义的代码 class B.
我添加第二种替代方式
//extendsClass function using for create temporary extendedfirst argument baseClass ,other arguments is classes using for inherit
function extendsClass(...cls)
{
let str="";
for (let i=arguments.length-1;i>0;i--)
{
str+=(i==1?"(":"")+(arguments[i-1]+"").replace(RegExp(arguments[i-1].name+"({|\s.*{)?"),arguments[i-1].name+" extends "+arguments[i].name+" {").replaceAll("//super","super")+(i==1?")":"");
}
return eval(str);
}
class A{
constructor(name)
{
this.name=name;
}
getname=function(){return this.name};
run(){console.log(`instance of A ${this instanceof A}`)};
}
class B {
constructor(surname,name)
{
//super(name);
this.surname=surname;
}
getsurname(){return this.surname};
run(){
//super.run();
console.log(`instance of B ${this instanceof B}`)};
}
class C {
constructor(name,surname)
{
//super(surname,name);
}
getfullname(){return this.name+" "+this.surname};
};
class D extends extendsClass(C,B,A) {
constructor(name,surname,address)
{
super(name,surname);
this.address=address;
}
}
//extendsClass function create temprory, class C extends from B,B extends from A, A class dont create temporary stay as global class.
var person=new (extendsClass(C,B,A))("Ed","Boon");
console.log(person.getname());//output Ed
console.log(person.getsurname());//output Boon
console.log(person.getfullname());//output Ed Boon
person.run();
var person2=new D("Cem","Firat","A place in the world");
console.log(person2.getname());//output Cem
console.log(person2.getsurname());//output Firat
console.log(person.getfullname());//output Cem Firat
person2.run();
我已经在 BabelJS and on MDN 上完成了我的大部分研究(根本没有任何信息),但如果我没有足够仔细地四处寻找更多信息,请随时告诉我关于 ES6 规范
我想知道 ES6 是否以与其他鸭子类型语言相同的方式支持多重继承。例如,我可以做类似的事情吗:
class Example extends ClassOne, ClassTwo {
constructor() {
}
}
将多个 class 扩展到新的 class?如果是这样,口译员会更喜欢 ClassTwo 的 methods/properties 而不是 ClassOne 吗?
这对于原型继承的工作方式来说是不可能的。让我们看看继承的 props 在 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
您可以使用 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
对
var a = {x: 1}
var o = Object.create(a);
o.x; // 1
a.y = 2;
o.y; // 2
一个对象只能有一个原型。从两个 classes 继承可以通过创建一个父对象作为两个父原型的组合来完成。
subclassing 的语法使得在声明中这样做成为可能,因为 extends
子句的右侧可以是任何表达式。因此,您可以编写一个根据您喜欢的任何标准组合原型的函数,并在 class 声明中调用该函数。
我想出了这些解决方案:
'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/class - 你将没有机会继续循环。
const EventEmitter = require( 'events' );
const A = require( './abstracts/a' )(EventEmitter);
const B = require( './abstracts/b' )(A);
const C = require( './abstracts/b' )(B);
在带有 --harmony 标志的节点 v5.4.1 中为我工作
检查下面的示例,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();
会打印出来
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
这是扩展多个 classes 的 awesome/really 蹩脚方法。我正在使用 Babel 放入我的转译代码中的几个函数。该函数创建一个新的 class 继承 class1,class1 继承 class2,依此类推。它有它的问题,但是一个有趣的想法。
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) {}
使用 Mixins 实现 ES6 多重继承。
let classTwo = Base => class extends Base{
// ClassTwo Code
};
class Example extends classTwo(ClassOne) {
constructor() {
}
}
从页面es6-features.org/#ClassInheritanceFromExpressions,可以编写一个允许多重继承的聚合函数:
class Rectangle extends aggregation(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
}
但这已经在像 aggregation.
这样的库中提供了使用 extent 和自定义函数来处理 es6 的多重继承
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)
好吧 Object.assign 让你有可能做一些接近的事情,尽管有点像用 ES6 classes 组合。
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
有一个 bite
方法,而只有它的原型有一个 eat
方法
Sergio Carneiro's and Jon's implementation 要求您为除一个 class 之外的所有对象定义初始化函数。这是聚合函数的修改版本,它在构造函数中使用默认参数。其中也包括一些我的评论。
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.
此聚合函数将优先选择 class 中稍后出现在 class 列表中的属性和方法。
没有简单的方法来进行多重 class 继承。我遵循关联和继承的组合来实现这种行为。
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());
希望这对您有所帮助。
Justin Fagnani describes 一种非常干净(恕我直言)的方式将多个 classes 组合成一个,因为在 ES2015 中,classes 可以用 [=46 创建=] 表达式.
表达式与声明
基本上,就像您可以使用表达式创建函数一样:
function myFunction() {} // function declaration
var myFunction = function(){} // function expression
你可以用 classes 做同样的事情:
class MyClass {} // class declaration
var MyClass = class {} // class expression
表达式是在运行时计算的,当代码执行时,而声明是事先执行的。
使用 class 表达式创建 mixin
您可以使用它来创建一个仅在调用该函数时才动态创建 class 的函数:
function createClassExtending(superclass) {
return class AwesomeClass extends superclass {
// you class body here as usual
}
}
它最酷的地方在于,您可以预先定义整个 class,并且只在您调用函数时决定应该扩展哪个 class:
class A {}
class B {}
var ExtendingA = createClassExtending(A)
var ExtendingB = createClassExtending(B)
如果你想将多个classes混合在一起,因为ES6 classes只支持单继承,你需要创建一个包含所有classes的链=46=]es 你想混合在一起。假设您想创建一个扩展 A 和 B 的 class C,您可以这样做:
class A {}
class B extends A {}
class C extends B {} // C extends both A and B
问题在于它非常静态。如果您后来决定要制作一个扩展 B 但不扩展 A 的 class D,那您就有问题了。
但是使用 classes 可以是表达式这一事实的一些聪明的技巧,你可以通过创建 A 和 B 来解决这个问题,而不是直接作为 classes,而是作为 class工厂(为简洁起见使用箭头函数):
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 包含在层次结构中的。
我根据这些原则制作了一个库,您可以看看:mics
这个 ES6 解决方案对我有用:
多个-inheritance.js
export function allOf(BaseClass, ...Mixins) {
function copyProperties(target, source) {
const allPropertyNames = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source))
allPropertyNames.forEach((propertyName) => {
if (propertyName.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
return
Object.defineProperty(target, propertyName, Object.getOwnPropertyDescriptor(source, propertyName))
})
}
class Base extends BaseClass
{
constructor (...args) {
super(...args)
Mixins.forEach((Mixin) => {
copyProperties(this, new Mixin(...args))
})
}
}
Mixins.forEach((mixin) => {
copyProperties(Base.prototype, Mixin.prototype)
})
return Base
}
main.js
import { allOf } from "./multiple-inheritance.js"
class A
{
constructor(name) {
this.name = name
}
sayA() {
return this.name
}
}
class B
{
constructor(name) {
this.name = name
}
sayB() {
return this.name
}
}
class AB extends allOf(A, B)
{
sayAB() {
return this.name
}
}
const ab = new AB("ab")
console.log("ab.sayA() = "+ab.sayA()+", ab.sayB() = "+ab.sayB()+", ab.sayAB() = "+ab.sayAB())
浏览器控制台的产量:
ab.sayA() = ab, ab.sayB() = ab, ab.sayAB() = ab
我也会添加我的解决方案 - 我发现它对我自己来说是最友好的,从我在这个帖子中读到的内容来看。
export const aggregate = (...mixins) => (Base) => {
const 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, mixin);
copyProps(Base.prototype, mixin.prototype);
});
return Base;
};
你可以像这样使用它:
class _MyBaseClass {}
const MyBaseClass = aggregate(ExtensionOne, ExtensionTwo)(_MyBaseClass);
作为概念验证,我做了以下功能。它需要一个 classes 列表,并将它们组合成一个新的 class(最后一个原型获胜,因此没有冲突)。创建组合函数时,用户可以选择使用所有原始构造函数 [sic!] 或传递自己的构造函数。这是这个实验的最大挑战:提出构造函数应该做什么的描述。将方法复制到原型中不是问题,但新组合对象的预期逻辑是什么。或者它应该是无构造函数的?在 Python 中,据我所知,它找到了 匹配的 构造函数,但 JS 中的函数更容易接受,因此可以将几乎所有内容传递给函数,并从它赢得的签名中传递说清楚。
我认为它没有优化,但目的是探索可能性。 instanceof
不会像预期的那样运行,我想这很糟糕,因为 class-oriented 开发人员喜欢将其用作工具。
也许JavaScript只是没有。
/*
(c) Jon Krazov 2019
Below is an experiment searching boundaries of JavaScript.
It allows to compute one class out of many classes.
Usage 1: Without own constructor
If no constructor is passed then constructor of each class will be called
with params passed in object. In case of missing params, constructor
will be called without params.
Example:
const MyClass1 = computeClass([Class1, Class2, Class3]);
const myClass1Instance = new MyClass1({
'Class1': [1, 2],
'Class2': ['test'],
'Class3': [(value) => value],
});
Usage 2: With own constructor
If constructor is passed in options object (second param) then it will
be called in place of constructors of all classes.
Example:
const MyClass2 = computeClass([Class1, Class2, Class3], {
ownConstructor(param1) {
this.name = param1;
}
});
const myClass2Instance = new MyClass2('Geoffrey');
*/
// actual function
var computeClass = (classes = [], { ownConstructor = null } = {}) => {
const noConstructor = (value) => value != 'constructor';
const ComputedClass = ownConstructor === null
? class ComputedClass {
constructor(args) {
classes.forEach((Current) => {
const params = args[Current.name];
if (params) {
Object.assign(this, new Current(...params));
} else {
Object.assign(this, new Current());
}
})
}
}
: class ComputedClass {
constructor(...args) {
if (typeof ownConstructor != 'function') {
throw Error('ownConstructor has to be a function!');
}
ownConstructor.call(this, ...args);
}
};
const prototype = classes.reduce(
(composedPrototype, currentClass) => {
const partialPrototype = Object.getOwnPropertyNames(currentClass.prototype)
.reduce(
(result, propName) =>
noConstructor(propName)
? Object.assign(
result,
{ [propName]: currentClass.prototype[propName] }
)
: result,
{}
);
return Object.assign(composedPrototype, partialPrototype);
},
{}
);
Object.entries(prototype).forEach(([prop, value]) => {
Object.defineProperty(ComputedClass.prototype, prop, { value });
});
return ComputedClass;
}
// demo part
var A = class A {
constructor(a) {
this.a = a;
}
sayA() { console.log('I am saying A'); }
}
var B = class B {
constructor(b) {
this.b = b;
}
sayB() { console.log('I am saying B'); }
}
console.log('class A', A);
console.log('class B', B);
var C = computeClass([A, B]);
console.log('Composed class');
console.log('var C = computeClass([A, B]);', C);
console.log('C.prototype', C.prototype);
var c = new C({ A: [2], B: [32] });
console.log('var c = new C({ A: [2], B: [32] })', c);
console.log('c instanceof A', c instanceof A);
console.log('c instanceof B', c instanceof B);
console.log('Now c will say:')
c.sayA();
c.sayB();
console.log('---');
var D = computeClass([A, B], {
ownConstructor(c) {
this.c = c;
}
});
console.log(`var D = computeClass([A, B], {
ownConstructor(c) {
this.c = c;
}
});`);
var d = new D(42);
console.log('var d = new D(42)', d);
console.log('Now d will say:')
d.sayA();
d.sayB();
console.log('---');
var E = computeClass();
console.log('var E = computeClass();', E);
var e = new E();
console.log('var e = new E()', e);
最初发布 here(gist.github.com)。
我花了半周的时间自己弄明白了,并写了一整篇文章,https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS,希望对你们中的一些人有所帮助。
简而言之,以下是如何在 JavaScript 中实现 MI:
class Car {
constructor(brand) {
this.carname = brand;
}
show() {
return 'I have a ' + this.carname;
}
}
class Asset {
constructor(price) {
this.price = price;
}
show() {
return 'its estimated price is ' + this.price;
}
}
class Model_i1 { // extends Car and Asset (just a comment for ourselves)
//
constructor(brand, price, usefulness) {
specialize_with(this, new Car(brand));
specialize_with(this, new Asset(price));
this.usefulness = usefulness;
}
show() {
return Car.prototype.show.call(this) + ", " + Asset.prototype.show.call(this) + ", Model_i1";
}
}
mycar = new Model_i1("Ford Mustang", "0K", 16);
document.getElementById("demo").innerHTML = mycar.show();
这里是 specialize_with() 一行:
function specialize_with(o, S) { for (var prop in S) { o[prop] = S[prop]; } }
在javascript中你不能给class(构造函数)2个不同的原型对象并且因为javascript中的继承与原型一起工作所以你不能使用超过1个继承对于一个 class 但 你可以聚合并加入 Prototype 对象的 属性 和 class 中的主要 属性 手动重构parent classes and next extends that new version and join class to your target class 有你的问题的代码:
let Join = (...classList) => {
class AggregatorClass {
constructor() {
classList.forEach((classItem, index) => {
let propNames = Object.getOwnPropertyNames(classItem.prototype);
propNames.forEach(name => {
if (name !== 'constructor') {
AggregatorClass.prototype[name] = classItem.prototype[name];
}
});
});
classList.forEach(constructor => {
Object.assign(AggregatorClass.prototype, new constructor())
});
}
}
return AggregatorClass
};
https://www.npmjs.com/package/ts-mixer
拥有最好的 TS 支持和许多其他有用的功能!
我的答案似乎代码更少而且对我有用:
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!
我一直在使用这样的模式来编写复杂的多继承事物:
var mammal = {
lungCapacity: 200,
breath() {return 'Breathing with ' + this.lungCapacity + ' capacity.'}
}
var dog = {
catchTime: 2,
bark() {return 'woof'},
playCatch() {return 'Catched the ball in ' + this.catchTime + ' seconds!'}
}
var robot = {
beep() {return 'Boop'}
}
var robotDogProto = Object.assign({}, robot, dog, {catchTime: 0.1})
var robotDog = Object.create(robotDogProto)
var livingDogProto = Object.assign({}, mammal, dog)
var livingDog = Object.create(livingDogProto)
此方法使用的代码很少,并允许覆盖默认属性(就像我在 robotDogProto 中使用自定义 catchTime 所做的那样)
下面的解决方案(class 通过复制实例字段和原型属性进行克隆)对我有用。我正在使用普通的 JS(即不是 Typescript),带有 JsDoc 注释和 VSCode 用于编译时类型检查。
求解界面很简单:
class DerivedBase extends cloneClass(Derived, Base) {}
//To add another superclass:
//class Der1Der2Base extends cloneClass(Derived2, DerivedBase)
let o = new DerivedBase()
所涉及的 classes 可以像普通 ES6 classes 一样创建。
但是需要完成以下操作:
- 使用 isInstanceOf() 而不是内置运算符 instanceof。
- 不要使用 'super' 调用基类 class 的非构造函数成员,而是使用函数 super2()。
- 不要在构造函数中编写任何代码,而是将其写入名为 'init()' 的 class 方法中,该方法依次由构造函数调用。请参阅下面的示例。
/* Paste the entire following text into the browser's dev console */
/* Tested on latest version of Chrome, Microsoft Edge and FireFox (Win OS)*/
/* Works with JSDoc in VSCode */
/* Not tested on minified/obfuscated code */
//#region library
const log = console.log
/**
* abbreviation for Object.getPrototypeOf()
* @param {any} o
*/
function proto(o) { return Object.getPrototypeOf(o) }
/** @param {function} fn */
function callIfNonNull(fn) { if (fn != null) { return fn() } }
/**
* @param {boolean} b
* @param {()=>string} [msgFn]
*/
function assert(b, msgFn) {
if (b) { return }
throw new Error('assert failed: ' + ((msgFn == null) ? '' : msgFn()))
}
/** @param {any} o */
function asAny(o) { return o }
/**
* Use this function instead of super.<functionName>
* @param {any} obj
* @param {string} attr the name of the function/getter/setter
* @param {any} cls the class for the current method
* @param {any[]} args arguments to the function/getter/setter
*/
function super2(obj, attr, cls, ...args) {
let nms = clsNms(obj)
assert(nms[0] == nms[1])
const objCls = proto(obj)
const superObj = proto(ancestorNamed(objCls, cls.name))
assert(superObj != obj)
const attrDscr = getOwnOrBasePropDscr(superObj, attr)
if (attrDscr == null) { return null }
let attrVal = attrDscr['value']
const attrGet = attrDscr['get']
const attrSet = attrDscr['set']
if (attrVal == null) {
if (attrGet != null) {
if (attrSet != null) {
assert([0, 1].includes(args.length))
attrVal = ((args.length == 0) ? attrGet : attrSet)
} else {
assert(args.length == 0,
() => 'attr=' + attr + ', args=' + args)
attrVal = attrGet
}
} else if (attrSet != null) {
assert(args.length == 1)
attrVal = attrSet
} else {
assert(false)//no get or set or value!!!!
}
assert(typeof attrVal == 'function')
}
if (typeof attrVal != 'function') { return attrVal }
const boundFn = attrVal.bind(obj)
return boundFn(...args)
}
/**
* Use this function to call own prop instead of overriden prop
* @param {any} obj
* @param {string} attr the name of the function/getter/setter
* @param {any} cls the class for the current method
* @param {any[]} args arguments to the function/getter/setter
*/
function ownProp(obj, attr, cls, ...args) {
let protoObj = ancestorNamed(proto(obj), cls.name)
const attrDscr = Object.getOwnPropertyDescriptor(protoObj, attr)
if (attrDscr == null) {
log(`ownProp(): own property '${attr}' does not exist...`)
return null
}
let attrVal = attrDscr['value']
const attrGet = attrDscr['get']
const attrSet = attrDscr['set']
if (attrVal == null) {
if (attrGet != null) {
if (attrSet != null) {
assert([0, 1].includes(args.length))
attrVal = ((args.length == 0) ? attrGet : attrSet)
} else {
assert(args.length == 0,
() => 'attr=' + attr + ', args=' + args)
attrVal = attrGet
}
} else if (attrSet != null) {
assert(args.length == 1)
attrVal = attrSet
} else {
assert(false)//no get or set or value!!!!
}
assert(typeof attrVal == 'function')
}
if (typeof attrVal != 'function') {
log(`ownProp(): own property '${attr}' not a fn...`)
return attrVal
}
const boundFn = attrVal.bind(obj)
return boundFn(...args)
}
/**
* @param {any} obj
* @param {string} nm
*/
function getOwnOrBasePropDscr(obj, nm) {
let rv = Object.getOwnPropertyDescriptor(obj, nm)
if (rv != null) { return rv }
let protObj = proto(obj)
if (protObj == null) { return null }
return getOwnOrBasePropDscr(protObj, nm)
}
/**
* @param {any} obj
* @param {string} nm
*/
function ancestorNamed(obj, nm) {
const ancs = ancestors(obj)
for (const e of ancs) {
if ((e.name || e.constructor.name) == nm) { return e }
}
}
/**
* @template typeOfDerivedCls
* @template typeOfBaseCls
* @param {typeOfDerivedCls} derivedCls
* @param {typeOfBaseCls} baseCls
* @returns {typeOfDerivedCls & typeOfBaseCls}
*/
function cloneClass(derivedCls, baseCls) {
const derClsNm = derivedCls['name'], baseClsNm = baseCls['name']
const gbl = globalThis
//prevent unwanted cloning and circular inheritance:
if (isInstanceOf(baseCls, asAny(derivedCls))) { return asAny(baseCls) }
if (isInstanceOf(derivedCls, asAny(baseCls))) { return asAny(derivedCls) }
//Object does not derive from anything; it is the other way round:
if (derClsNm == 'Object') { return cloneClass(baseCls, derivedCls) }
//use cached cloned classes if available
if (gbl.clonedClasses == null) { gbl.clonedClasses = {} }
const k = derClsNm + '_' + baseClsNm, kVal = gbl.clonedClasses[k]
if (kVal != null) { return kVal }
//clone the base class of the derived class (cloning occurs only if needed)
let derBase = cloneClass(proto(derivedCls), baseCls)
//clone the derived class
const Clone = class Clone extends derBase {
/** @param {any[]} args */
constructor(...args) {
super(...args)
ownProp(this, 'init', Clone, ...args)
}
}
//clone the properties of the derived class
Object.getOwnPropertyNames(derivedCls['prototype'])
.filter(prop => prop != 'constructor')
.forEach(prop => {
const valToSet =
Object.getOwnPropertyDescriptor(derivedCls['prototype'], prop)
if (typeof valToSet == 'undefined') { return }
Object.defineProperty(Clone.prototype, prop, valToSet)
})
//set the name of the cloned class to the same name as its source class:
Object.defineProperty(Clone, 'name', { value: derClsNm, writable: true })
//cache the cloned class created
gbl.clonedClasses[k] = Clone
log('Created a cloned class with id ' + k + '...')
return asAny(Clone)
}
/**
* don't use instanceof throughout your application, use this fn instead
* @param {any} obj
* @param {Function} cls
*/
function isInstanceOf(obj, cls) {
if (obj instanceof cls) { return true }
return clsNms(obj).includes(cls.name)
}
/** @param {any} obj */
function clsNms(obj) {
return ancestors(obj).map(/** @param {any} e */ e =>
e.name || e.constructor.name)
}
/**
* From: https://gist.github.com/ceving/2fa45caa47858ff7c639147542d71f9f
* Returns the list of ancestor classes.
*
* Example:
* ancestors(HTMLElement).map(e => e.name || e.constructor.name)
* => ["HTMLElement", "Element", "Node", "EventTarget", "Function", "Object"]
* @param {any} anyclass
*/
function ancestors(anyclass) {
if (anyclass == null) { return [] }
return [anyclass, ...(ancestors(proto(anyclass)))]
}
//#endregion library
//#region testData
class Base extends Object {
/** @param {any[]} args */
constructor(...args) {//preferably accept any input
super(...args)
ownProp(this, 'init', Base, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class Base...')
//TODO: add code here to get the args as a named dictionary
//OR, follow a practice of parameterless constructors and
//initial-value-getting methods for class field intialization
/** example data field of the base class */
this.baseClsFld = 'baseClsFldVal'
}
m1() { log('Executed base class method m1') }
b1() { log('Executed base class method b1') }
get baseProp() { return 'basePropVal' }
}
class Derived extends Object {//extend Object to allow use of 'super'
/** @param {any[]} args */
constructor(...args) {//convention: accept any input
super(...args)
ownProp(this, 'init', Derived, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class Derived...')
this.derClsFld = 'derclsFldVal'
}
m1() {
const log = /** @param {any[]} args */(...args) =>
console.log('Derived::m1(): ', ...args)
log(`super['m1']: `, super['m1'])
super2(this, 'm1', Derived)
log(`super['baseProp']`, super['baseProp'])
log(`super2(this, 'baseProp', Derived)`,
super2(this, 'baseProp', Derived))
log(`super2(this, 'nonExistentBaseProp', Derived)`,
super2(this, 'nonExistentBaseProp', Derived))
}
m2() {
log('Executed derived class method 2')
}
}
class DerivedBase extends cloneClass(Derived, Base) {
/** @param {any[]} args */
constructor(...args) {
super(...args)
ownProp(this, 'init', DerivedBase, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class DerivedBase...')
}
}
log('Before defining o (object of DerivedBase)...')
let o = new DerivedBase()
log('After defining o (object of DerivedBase)...')
class Derived2 extends Base {
/** @param {any} args */
constructor(...args) {
//convention/best practice: use passthrough constructors for the classes
//you write
super(...args)
ownProp(this, 'init', Derived2, ...args)
}
/**
* @param {any[]} _args
*/
init(..._args) {
log('Executing init() of class Derived2...')
}
derived2func() { log('Executed Derived2::derived2func()') }
}
class Der1Der2Base extends cloneClass(Derived2, DerivedBase) {
/** @param {any} args */
constructor(...args) {
//convention/best practice: use passthrough constructors for the classes
//you write
super(...args)
ownProp(this, 'init', Der1Der2Base, ...args)
}
/** @param {any[]} _args */
init(..._args) {
log('Executing original ctor of class Der1Der2Base...')
}
}
log('Before defining o2...')
const o2 = new Der1Der2Base()
log('After defining o2...')
class NotInInheritanceChain { }
//#endregion testData
log('Testing fields...')
log('o.derClsFld:', o.derClsFld)
log('o.baseClsFld:', o.baseClsFld)
//o.f3 JSDoc gives error in VSCode
log('Test method calls')
o.b1()
o.m1()
o.m2()
//o.m3() //JSDoc gives error in VSCode
log('Test object o2')
o2.b1()
o2.m1()
o2.derived2func()
//don't use instanceof throughout your application, use this fn instead
log('isInstanceOf(o,DerivedBase)', isInstanceOf(o, DerivedBase))
log('isInstanceOf(o,Derived)', isInstanceOf(o, Derived))
log('isInstanceOf(o,Base)', isInstanceOf(o, Base))
log('isInstanceOf(o,NotInInheritanceChain)',
isInstanceOf(o, NotInInheritanceChain))
注意事项:JSDoc 交集运算符 & 可能并不总是有效。在这种情况下,可能需要使用其他一些解决方案。例如,可能需要定义一个单独的接口 class,它将 'manually' 组合 2 个 class。此接口 class 可以从其中一个 class 扩展,另一个 class 的接口可以使用 VsCode 快速修复选项自动实现。
回答
这是不可能的。一个对象在Javascript.
中只能有一个prototype
解决方法
您可以使用 Object.assign
,它可以工作,但不安全并且不提供自动完成或任何类型安全。
class Example {
constructor (props) {
Object.assign(this, new Class1(props))
Object.assign(this, new Class2(props))
}
}
您应该考虑的另一种方式
在这个例子中,我假设扩展 many 的目标是能够在一行中构建 Example
class,然后可以访问所有可能的方法-扩展 classes.
可选:如果方法依赖于超class中的方法,我建议创建另一个BaseClass
并有class Class1 extends BaseClass {}
。这样 Example
的可重用部分就不会被重写。
class Class1 extends BaseClass {}
class Class2 extends BaseClass {}
class Example {
class1: Class1 // Sorry for the Typescript
class2: Class2
constructor (props) {
this.class1 = new Class1(props)
this.class2 = new Class2(props)
}
}
const example = new Example(props)
example.class1.someMethod()
我建议使用组合而不是使用继承,它更灵活,并且您可以获得相同的收益,即在不同的地方重用相同的代码 类。
import Controller from 'your-favorite-framework';
class Example extends ClassTwoMixin(ClassOneMixin(Controller)) {
constructor() {
}
}
const ClassOneMixin = (superclass) => class extends superclass {}
const ClassTwoMixin = (superclass) => class extends superclass {}
要了解更多信息,请搜索:“组合优于继承”
我写作是为了理解和增加 js 编程的灵活性。这段代码是参考上面的代码写的
class A{
constructor(name)
{
this.name=name;
}
getname=function(){return this.name};
}
B=(x)=>(class B extends (()=>x||Object)(){
constructor(surname,name)
{ super(name);
this.surname=surname;
}
getsurname(){return this.surname};
})
class C extends B(A){
constructor(name,surname)
{
super(surname,name);
}
getfullname(){return this.name+" "+this.surname};
};
let person=new C("Ed","Boon");
console.log(person.getname());//output Ed
console.log(person.getsurname());//output Boon
console.log(person.getfullname());//output Ed Boon
console.log(person);
console.log(person.__proto__.constructor); //person.__proto__ properties inherit from C class
console.log(person.__proto__.__proto__.constructor); //person.__proto__.__proto__ properties inherit from B class
console.log(person.__proto__.__proto__.__proto__.constructor); //person.__proto__.__proto__ .__proto__ properties inherit from A class
我添加第二种替代方式
//extendsClass function using for create temporary extendedfirst argument baseClass ,other arguments is classes using for inherit
function extendsClass(...cls)
{
let str="";
for (let i=arguments.length-1;i>0;i--)
{
str+=(i==1?"(":"")+(arguments[i-1]+"").replace(RegExp(arguments[i-1].name+"({|\s.*{)?"),arguments[i-1].name+" extends "+arguments[i].name+" {").replaceAll("//super","super")+(i==1?")":"");
}
return eval(str);
}
class A{
constructor(name)
{
this.name=name;
}
getname=function(){return this.name};
run(){console.log(`instance of A ${this instanceof A}`)};
}
class B {
constructor(surname,name)
{
//super(name);
this.surname=surname;
}
getsurname(){return this.surname};
run(){
//super.run();
console.log(`instance of B ${this instanceof B}`)};
}
class C {
constructor(name,surname)
{
//super(surname,name);
}
getfullname(){return this.name+" "+this.surname};
};
class D extends extendsClass(C,B,A) {
constructor(name,surname,address)
{
super(name,surname);
this.address=address;
}
}
//extendsClass function create temprory, class C extends from B,B extends from A, A class dont create temporary stay as global class.
var person=new (extendsClass(C,B,A))("Ed","Boon");
console.log(person.getname());//output Ed
console.log(person.getsurname());//output Boon
console.log(person.getfullname());//output Ed Boon
person.run();
var person2=new D("Cem","Firat","A place in the world");
console.log(person2.getname());//output Cem
console.log(person2.getsurname());//output Firat
console.log(person.getfullname());//output Cem Firat
person2.run();