Is it possible to extend only some specific part from multiple classes? Example:
Having a look on the provided example, I immediately would suggest "... please consider decomposition."
For the given code it was extracting both behaviors "walk" and "run". Thus a Walker
might end up as a hypothetical Person
withWalkingAbility
, a Runner
then likewise becomes a Person
withRunningAbility
.
All the // more functions
within the originally provided Walker
and Runner
code then could be implemented by a Person
class.
var
withSelfAwareWalkingAbility = (function mixinFactory () { // does create a closure ...
function walking() { //
console.log("I'm walking"); //
} //
return function abilityMixin () { //
this.walk = walking; // shared code. // ... but shares its
}; // *ability* implementation.
}()),
withSelfAwareRunningAbility = (function mixinFactory () { // does create a closure ...
function running() { //
console.log("I'm running"); //
} //
return function abilityMixin () { //
this.run = running; // ... but shares its
}; // *ability* implementation.
}());
class Person {
constructor() {
// `Person` specific instance slots.
}
// `Person` specific prototypal methods.
}
A first big win was that one now is in control of where to place the composition, either at prototype
level or within the constructor
.
It depends on a Walker
's / Runner
's final design and can be answered best by the OP her/himself.
class Walker extends Person {
constructor() {
super();
withSelfAwareWalkingAbility.call(this); // composition at object level / instantiation time.
}
}
... versus ...
class Runner extends Person {
constructor() {
super();
}
}
withSelfAwareRunningAbility.call(Runner.prototype); // composition at a `Runner`'s `prototype` slot.
var
withSelfAwareWalkingAbility = (function mixinFactory () {
function walking() {
console.log("I'm walking");
}
return function abilityMixin () {
this.walk = walking;
};
}()),
withSelfAwareRunningAbility = (function mixinFactory () {
function running() {
console.log("I'm running");
}
return function abilityMixin () {
this.run = running;
};
}());
class Person {
constructor() {
// `Person` specific instance slots.
}
// `Person` specific prototypal methods.
}
class Walker extends Person {
constructor() {
super();
withSelfAwareWalkingAbility.call(this);
}
}
class Runner extends Person {
constructor() {
super();
}
}
withSelfAwareRunningAbility.call(Runner.prototype);
var
walker = (new Walker),
runner = (new Runner);
console.log('walker : ', walker); // Walker {walk: function}
console.log('(walker instanceof Walker) ? ', (walker instanceof Walker)); // true
console.log('(walker instanceof Person) ? ', (walker instanceof Person)); // true
console.log('(walker instanceof Runner) ? ', (walker instanceof Runner)); // false
walker.walk(); // "I'm walking"
console.log('(walker.hasOwnProperty("walk")) ? ', walker.hasOwnProperty("walk")); // true
console.log('\n');
console.log('runner : ', runner); // Runner {}
console.log('(runner instanceof Runner) ? ', (runner instanceof Runner)); // true
console.log('(runner instanceof Person) ? ', (runner instanceof Person)); // true
console.log('(runner instanceof Walker) ? ', (runner instanceof Walker)); // false
runner.run(); // "I'm running"
console.log('(runner.hasOwnProperty("run")) ? ', runner.hasOwnProperty("run")); // false
.as-console-wrapper { max-height: 100%!important; top: 0; }
Another win of decomposing behavior into smaller or even atomic "composable units of reuse"[^1] comes from the easiness of composing them again into bigger tailored mixins/traits like ...
function withSelfAwareWalkingAndRunningAbility() {
withSelfAwareWalkingAbility.call(this);
withSelfAwareRunningAbility.call(this);
}
[^1]: SCG, University of Bern: »Talents: Dynamically Composable Units of Reuse«
Note ... there is now a second part of this answer providing the example from above again, but with a real trait approach backed by a library