/*
(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);