Felix Kling's comment outlines the closest you'll get to a tidy solution for this. It uses two ES6 features—Object.assign
and the object literal property value shorthand.
Here's an example with tree
and pot
as the instance properties:
class ChristmasTree {
constructor(tree, pot, tinsel, topper) {
Object.assign(this, { tree, pot });
this.decorate(tinsel, topper);
}
decorate(tinsel, topper) {
// Make it fabulous!
}
}
Of course, this isn't really what you wanted; you still need to repeat the argument names, for one thing. I had a go at writing a helper method which might be a bit closer…
Object.autoAssign = function(fn, args) {
// Match language expressions.
const COMMENT = ///.*$|/*[sS]*?*//mg;
const ARGUMENT = /([^s,]+)/g;
// Extract constructor arguments.
const dfn = fn.constructor.toString().replace(COMMENT, '');
const argList = dfn.slice(dfn.indexOf('(') + 1, dfn.indexOf(')'));
const names = argList.match(ARGUMENT) || [];
const toAssign = names.reduce((assigned, name, i) => {
let val = args[i];
// Rest arguments.
if (name.indexOf('...') === 0) {
name = name.slice(3);
val = Array.from(args).slice(i);
}
if (name.indexOf('_') === 0) { assigned[name.slice(1)] = val; }
return assigned;
}, {});
if (Object.keys(toAssign).length > 0) { Object.assign(fn, toAssign); }
};
This auto-assigns any parameters whose names are prefixed with an underscore to instance properties:
constructor(_tree, _pot, tinsel, topper) {
// Equivalent to: Object.assign({ tree: _tree, pot: _pot });
Object.autoAssign(this, arguments);
// ...
}
It supports rest parameters, but I omitted support for default parameters. Their versatility, coupled with JS' anaemic regular expressions, makes it hard to support more than a small subset of them.
Personally, I wouldn't do this. If there were a native way to reflect on the formal arguments of a function, this would be really easy. As it is, it's a mess, and doesn't strike me as a significant improvement over Object.assign
.