Multiple inheritance using classes

2020-02-06 18:13发布

问题:

Is it possible to extend only some specific part from multiple classes? Example:

class Walker {
  walk() {
    console.log("I am walking");
  }
  // more functions
}
class Runner {
  run() {
    console.log("I am running");
  }
  // more functions
}

// Make a class that inherits only the run and walk functions

回答1:

You can pick and choose which methods from other classes you want to add to an existing class as long as your new object has the instance data and methods that the methods you are adding expect to be on that object.

class Walker {
  constructor() {}
  walk() {
    console.log("I am walking");
  }
  // more functions
}

class Runner {
  constructor() {}
  run() {
    console.log("I am running");
  }
  // more functions
}

class Participant {
  constructor() {}
}

// add methods from other classes to the Participant class
Participant.prototype.run = Runner.prototype.run;
Participant.prototype.walk = Walker.prototype.walk;

Keep in mind that methods are just functions that are properties on the prototype. So, you can assign any functions to the prototype that you want as long as the object you put them on has the right supporting instance data or other methods that those newly added methods might need.


Of course, if we understood more of your overall problem, you may also be able to solve your problem with more classical inheritance, but you can't use inheritance to solve it the exact way you asked to solve it.

Standard inheritance inherits all the methods of the base class. If you just want some of them, you will have to modify the prototype object manually to put just the methods you want there.

In the future, you will get a better set of answers if you describe the problem you're trying to solve rather than the solution you're trying to implement. That lets us understand what you're really trying to accomplish and allows us to offer solutions you haven't even thought of yet.



回答2:

Use composition:

class Walker {
  walk() {
    console.log("I am walking");
  }
// more functions
}
class Runner {
  run() {
    console.log("I am running");
  } 
  // more functions
}
class Composite {
  run() {
    new Runner().run();
  }
  walk() {
    new Walker().walk();
  }
}


回答3:

Note ... I had to split my previous answer into 2 separate parts since for quite a while it is not anymore that easy referencing outside hosted javascript code. Due to that I now do provide the necessary trait library as minified inline code within the next given example ...

Having on hand natively implemented Talents or Traits one day in JavaScript, will make this kind of composition even more flexible/powerful ...

var
  withWalkingAbility = (function mixinFactory () {
    function walking() {
      return "walking";
    }
    return function abilityMixin () {
      this.walk = walking;
    };
  }()),

  withRunningAbility = Trait.create(function trait (use, applicator) {
    function running() {
      return "running";
    }
    applicator(function () {
      this.run = running;
    });
  });

var
  withSelfAwareness = (function mixinFactory () {
    function reflectThySelf() {
      return "I'm";
    }
    return function awarenessMixin () {
      this.reflect = reflectThySelf;
    };
  }()),

  withDepersonalization = Trait.create(function trait (use, applicator) {
    function objectify() {
      return "It's";
    }
    applicator(function () {
      this.objectify = objectify;
    });
  });

var
  withSelfAwareWalkingAndRunningAbility = Trait.create(function trait (use, applicator) {

    use(withWalkingAbility, withRunningAbility)
      .apply.all();

    applicator(function () {

      this.walk = (function (proceed) {
        return function () {

          console.log([this.reflect(), proceed.call(this)].join(' '))
        };
      }(this.walk));

      this.run = (function (proceed) {
        return function () {

          console.log([this.reflect(), proceed.call(this)].join(' '))
        };
      }(this.run));

    }).requires([

      "reflect"

    ]);
  }),
  withDepersonalizedRunningAbility = Trait.create(function trait (use, applicator) {

    use(withRunningAbility)
      .apply(withRunningAbility);

    applicator(function () {

      this.run = (function (proceed) {
        return function () {

          console.log([this.objectify(), proceed.call(this)].join(' '))
        };
      }(this.run));

    }).requires([

      "objectify"

    ]);
  });



class Human {
  constructor() {
    // `Human` specific instance slots.
  }
  // `Human` specific prototypal methods.
}
withSelfAwareness.call(Human.prototype);


class Animal {
  constructor() {
    withDepersonalization.call(this);
    // other `Animal` specific instance slots.
  }
  // `Animal` specific prototypal methods.
}


class HumanWalkerAndRunner extends Human {
  constructor() {
    super();
  }
}
withSelfAwareWalkingAndRunningAbility.call(HumanWalkerAndRunner.prototype);


class AnimalRunner extends Animal {
  constructor() {
    super();
    withDepersonalizedRunningAbility.call(this);
  }
}


var
  humanWalkerAndRunner = (new HumanWalkerAndRunner),
  animalRunner = (new AnimalRunner);

console.log('humanWalkerAndRunner : ', humanWalkerAndRunner); // HumanWalkerAndRunner {}
console.log('(humanWalkerAndRunner instanceof HumanWalkerAndRunner) ? ', (humanWalkerAndRunner instanceof HumanWalkerAndRunner)); // true
console.log('(humanWalkerAndRunner instanceof Human) ? ', (humanWalkerAndRunner instanceof Human)); // true
humanWalkerAndRunner.walk(); //  "I'm walking"
humanWalkerAndRunner.run(); //  "I'm running"
console.log('(humanWalkerAndRunner.hasOwnProperty("walk")) ? ', humanWalkerAndRunner.hasOwnProperty("walk")); // false
console.log('(humanWalkerAndRunner.hasOwnProperty("run")) ? ', humanWalkerAndRunner.hasOwnProperty("run")); // false

console.log('\n');

console.log('animalRunner : ', animalRunner); // AnimalRunner {objectify: function, run: function}
console.log('(animalRunner instanceof AnimalRunner) ? ', (animalRunner instanceof AnimalRunner)); // true
console.log('(animalRunner instanceof Animal) ? ', (animalRunner instanceof Animal)); // true
animalRunner.run(); //  "It's running"
console.log('(animalRunner.hasOwnProperty("run")) ? ', animalRunner.hasOwnProperty("run")); // true
.as-console-wrapper { max-height: 100%!important; top: 0; }
<script>(function(r){function g(a){var c="none",b;Y(a)&&(ta(a)?c=ua:(b=Z(a),t("^class\\s+"+a.name+"\\s+\\{").test(b)?c=va:t("\\([^)]*\\)\\s+=>\\s+\\(").test(b)?c=wa:z(a)&&(c=xa(a)?ya:za(a)||Aa(a)||Ba(a)?Ca:aa)));return c===aa}function C(a){return["^\\[object\\s+",a,"\\]$"].join("")}function v(a,c){var b=Da[c];return!!b&&0<=b.indexOf(a)}function M(a,c,b){return function(){var d=arguments;c.apply(b,d);return a.apply(b,d)}}function N(a,c,b){return function(){var d=arguments,e=a.apply(b,d);c.apply(b,d);return e}}function Ea(a,c,b,d){return function(){var e=arguments;c.call(b,e,d);return a.apply(b,e)}}function Fa(a,c,b,d){return function(){var e=arguments,f=a.apply(b,e);c.call(b,e,d);return f}}function Ga(a,c,b,d){return function(){var e=arguments,f=a.apply(b,e);c.call(b,f,e,d);return f}}function Ha(a,c,b,d){return function(){var e=arguments,f;try{f=a.apply(b,e)}catch(l){c.call(b,l,e,d)}return f}}function Ia(a,c,b,d){return function(){var e=arguments,f,g;try{f=a.apply(b,e)}catch(m){g=m}c.call(b,g||f,e,d);return f}}function Ja(a,c,b,d){return function(){return c.call(b,a,c,arguments,d)}}function Ka(a,c){return function(){var b=this[a];g(b)||(b=u);this[a]=M(b,c,this)}}function La(a,c){return function(){var b=this[a];g(b)||(b=u);this[a]=N(b,c,this)}}function Ma(a,c){return function(b){var d=this[a];g(d)||(d=u);this[a]=Ea(d,c,this,b)}}function Na(a,c){return function(b){var d=this[a];g(d)||(d=u);this[a]=Fa(d,c,this,b)}}function Oa(a,c){return function(b){var d=this[a];g(d)||(d=u);this[a]=Ga(d,c,this,b)}}function Pa(a,c){return function(b){var d=this[a];g(d)||(d=u);this[a]=Ha(d,c,this,b)}}function Qa(a,c){return function(b){var d=this[a];g(d)||(d=u);this[a]=Ia(d,c,this,b)}}function Ra(a,c){return function(b){var d=this[a];g(d)||(d=u);this[a]=Ja(d,c,this,b)}}function ba(a,c,b){a.useTraits.rules.push(new A({type:"without",traitList:c,methodNameList:b}))}function ca(a,c,b,d){a.useTraits.rules.push(new A({type:"as",trait:c,methodName:b,methodAlias:d}))}function da(a){return ea(p(a)).reduce(function(a,b){q(b)&&a.push(F(b));return a},[])}function fa(){var a,c,b,d,e;if(O(this))if(d=this.valueOf(),e=d.parentLink,w(e))if(b=e.getSetup(),a=b.chainData,c=a.recentCalleeName,v(c,"without"))if(c=da(arguments),1<=c.length)a.recentCalleeName="without",ba(b,d.traitList,c);else throw new h("Not even a single <String> type was passed to 'without' as to be excluded method name.");else{a=["\u2026 invalid chaining of '",c,"().without()'"].join("");if("applyBehavior"==c)throw new n([a," in case of excluding one or more certain behaviors."].join(""));throw new n(a);}else throw new k("Please do not spoof the context of 'use'.");else throw new k("Please do not spoof the context of 'apply'.");return e}function P(a,c){var b=a.getSetup();if(Sa(c,a.valueOf().traitList))b.chainData.recentCalleeName="applyTraits",b=new Q({traitList:c,parentLink:a}),a.putChild(b);else throw new h("Any trait that got passed to 'apply' needs to be registered before via 'use'.");return b}function Sa(a,c){return a.every(function(a){return 0<=c.indexOf(a)})}function Ta(){var a;a=this.getSetup();var c=this.valueOf().traitList;I(this,a,a.chainData.recentCalleeName);a=P(this,c);return fa.apply(a,arguments)}function Ua(){var a=this.getSetup(),c=this.valueOf().traitList;I(this,a,a.chainData.recentCalleeName);return P(this,c)}function ga(){var a,c,b;b=p(arguments);if(w(this))if(a=this.getSetup(),c=a.chainData,c=c.recentCalleeName,I(this,a,c),a=["\u2026 invalid chaining of '",c,"().apply()'"].join(""),v(c,"apply"))if(x(b[0]))if(q(b[1]))if(v(c,"applyBehavior")){c=b[0];b=F(b[1]);a=this.getSetup();var d;if(0<=this.valueOf().traitList.indexOf(c))if(d={},c.call(d),g(d[b]))a.chainData.recentCalleeName="applyBehavior",b=new R({trait:c,methodName:b,parentLink:this}),this.putChild(b);else throw new k(["The requested '",b,"' method has not been implemented by the trait that got passed to 'apply'."].join(""));else throw new h("Any trait that got passed to 'apply' needs to be registered before via 'use'.");}else throw new n([a," in case of applying just a certain behavior."].join(""));else if(p(b).every(x))if(v(c,"applyTraits"))b=P(this,b);else throw new n([a," in case of applying from one or more traits."].join(""));else throw new h("'apply(\u2026)' excepts either, as its 2nd delimiting argument, just a 'String' type or exclusively one or more 'Trait' and 'Function' types.");else throw new h("'apply(<Trait|Function>, \u2026)' excepts as its 1st argument explicitly either a 'Trait' or a 'Function' type.");else throw new n(a);else throw new k("'use(\u2026).apply(\u2026)' only works within a validly chained context, please do not try spoofing the latter.");return b}function ha(){var a;a=this;if(O(a)||G(a))a=a.valueOf(),a=a.parentLink,a=ga.apply(a,arguments);else throw new k("Please do not spoof the context of 'apply'.");return a}function I(a,c,b){var d,e;a&&!c.chainData.isTerminated&&(d=a.getChild())&&(e=d.valueOf())&&("applyTraits"==b&&O(d)?ba(c,e.traitList,[]):"applyBehavior"==b&&G(d)&&(a=e.methodName,ca(c,e.trait,a,a)));c.chainData.recentCalleeName="apply"}function R(a){this.valueOf=function(){return{trait:a.trait,methodName:a.methodName,parentLink:a.parentLink}};this.toString=function(){return["ApplyLink::singleBehavior :: ",S.stringify(a)].join("")};return this}function G(a){var c;return null!=a&&(a instanceof R||g(a.as)&&g(a.after)&&g(a.before)&&g(a.valueOf)&&(c=a.valueOf())&&x(c.trait)&&q(c.methodName)&&w(c.parentLink))}function Q(a){this.valueOf=function(){return{traitList:a.traitList,parentLink:a.parentLink}};this.toString=function(){return["ApplyLink::fromTraits :: ",S.stringify(a)].join("")};return this}function O(a){var c;return null!=a&&(a instanceof Q||g(a.without)&&g(a.valueOf)&&(c=a.valueOf())&&T(c.traitList)&&w(c.parentLink))}function U(a,c){var b=null;this.getSetup=function(){return c};this.deleteChild=function(){b=null};this.putChild=function(a){b=a};this.getChild=function(){return b};this.valueOf=function(){return{traitList:p(a.traitList)}};this.toString=function(){return["UseRoot :: ",S.stringify(a)].join("")};this.apply=ga.bind(this);this.apply.all=Ua.bind(this);this.apply.all.without=Ta.bind(this);return this}function w(a){var c;return null!=a&&(a instanceof U||g(a.apply)&&g(a.valueOf)&&g(a.getSetup)&&(c=a.valueOf())&&T(c.traitList)&&(c=a.getSetup())&&ia(c))}function Va(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Ka(a,c));else throw new h("'before(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'before(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function Wa(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Ma(a,c));else throw new h("'before.stateful(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'before.stateful(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function Xa(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(La(a,c));else throw new h("'after(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'after(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function Ya(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Na(a,c));else throw new h("'after.stateful(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'after.stateful(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function Za(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Oa(a,c));else throw new h("'afterReturning(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'afterReturning(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function $a(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Pa(a,c));else throw new h("'afterThrowing(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'afterThrowing(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function ab(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Qa(a,c));else throw new h("'afterFinally(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'afterFinally(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function bb(a,c){var b=this.applicator,d=b.root,e=b.modifiers;b.canRequire=!1;if(q(a))if(g(c))e.push(Ra(a,c));else throw new h("'around(\u2026, <Function>)' excepts as its 2nd argument just a 'Function' type.");else throw new h("'around(<String>, \u2026)' excepts as its 1st argument just a 'String' type.");return d}function cb(){var a=this.applicator,c=a.root,b=a.didRequire;if(a.canRequire)if(a.canRequire=!1,a.didRequire=!0,b=da(arguments),1<=b.length)a.requires=b;else throw new h("Not even a single <String> type was passed to 'requires' as method name that a trait, at its apply time, expects to be present at least.");else{if(b)throw new n("'requires' can be invoked exactly once, right after having executed a trait's 'applicator' method.");throw new n("'requires' can not bee invoked after a modifier method, but has to be the direct follower of a trait's 'applicator' method.");}return c}function db(a){I(this.useTraits.root,this,this.chainData.recentCalleeName);this.chainData.isTerminated=!0;var c=this.applicator,b=this.useTraits.rules,d=0>=b.length;if(!d&&!ja(b))throw new k("The trait composition language's rule set does not result in applicable behavior. This happens e.g. if all behavior has been deleted via 'without'.");if(null!=a)if(g(a=F(a)))c.body=a,a.call(c.proxy);else throw new h("The applicator should either stay empty or receive a sole applicable type like a 'Trait' or a 'Function' type.");else d&&0>=c.modifiers.length&&D.warn("The trait descriptor till now did neither receive an applicable function via the 'applicator' method nor any composition rule via 'use(\u2026).apply(\u2026)' nor any method modifier.");return c.root}function eb(){var a;a=ea(p(arguments)).filter(function(a){return x(a)});if(1<=a.length)a=new U({traitList:a},this);else throw new h("Not even a single valid 'Trait' or 'Function' type has been provided to 'use(<Trait|Function>[, \u2026])'.");return this.useTraits.root=a}function y(a,c){return a.findIndex(function(a){return a.trait===c})}function ka(a){return{type:a.type,trait:a.trait,methodName:a.methodName}}function la(a){return{type:a.type,trait:a.trait,fromTrait:a.fromTrait,methodName:a.methodName}}function A(a){this.valueOf=function(){return fb[a.type](a)}}function ja(a){var c=!0;if(1<=a.length){var b={},c=ma(a),d=na(c);d.forEach(function(a){a.trait.call(a.proxy)});a.forEach(function(a){a.execute(b,d)});c=1<=J(b).length}return c}function gb(a,c,b){var d;if(b[0]!==oa&&(d=c.reduce(function(b,c){g(a[c])||b.push(c);return b},[]),1<=d.length))throw new h("The type misses following required method(s) \u2026 '"+d.join("', '")+"'.");return d}function hb(a,c,b){a.forEach(function(a){a.call(c,b)})}function ib(a){this.call=function(){var c=p(arguments),b;b=c.shift();b=null!=b&&b||null;a.apply(b,c);return b};this.apply=function(c,b){null==b&&(b=[]);c=null!=c&&c||null;a.apply(c,b);return c};return this}function jb(a){this.requires=function(){return p(a)};return this}function kb(a){var c=[],b={};a.call(b,oa);c=J(b).sort(function(a,b){return a<b&&-1||a>b&&1||0});b=null;this.keys=function(){return p(c)};return this}function pa(a,c){this.toString=function(){return"[object Trait]"};this.valueOf=function(){return a};ib.call(this,a);jb.call(this,c);kb.call(this,a);return this}function qa(a){var c;if(c=!!a)(c=a instanceof pa)||(c="object"==typeof a&&g(a.call)&&g(a.apply)&&g(a.valueOf)&&g(a.valueOf()));return c}function x(a){return qa(a)||g(a)}function ma(a){return a.reduce(function(a,b){var c=b.valueOf(),e=c.trait,f=c.fromTrait;e&&0>a.indexOf(e)&&a.push(e);f&&0>a.indexOf(f)&&a.push(f);(c.traitList||[]).forEach(function(b){0>a.indexOf(b)&&a.push(b)});return a},[])}function na(a){return a.map(function(a){return{trait:a,proxy:{}}})}function lb(a,c,b,d){var e=[],f={};if(!(0>=c.length||ja(c)))throw new k("The trait composition language's rule set does not result in applicable behavior. This happens e.g. if all behavior has been deleted via 'without'.");g(b)&&(b.call(f),e=J(f));return[a,c,e,d].some(function(a){return 1<=a.length})}function mb(a){var c,b=a.useTraits.rules,d=a.applicator;a=d.body;var e=d.requires,d=d.modifiers,f=ma(b);lb(f,b,a,d)&&(d=function(a,b,c,d,e){return function(){var f=this,h=p(arguments),l=na(a);l.forEach(function(a){a.trait.apply(a.proxy,h)});b.forEach(function(a){a.execute(f,l)});gb(f,c,h);g(d)&&d.apply(f,h);hb(e,f,h);return f}}(f,b,e,a,d),c=new pa(d,e));return c}var V=r.Function,H=r.Object,B=r.Array,t=r.RegExp,S=r.JSON,h=r.TypeError,k=r.ReferenceError,n=r.SyntaxError,nb=V.prototype,K=H.prototype,ra=U.prototype,sa=Q.prototype,L=R.prototype,oa={},aa="applicable-function",ya="buildin-constructor",va="class-constructor",wa="arrow-function",ua="generator-function",Ca="interface-type",Da={without:["apply","applyTraits"],as:["apply","applyBehavior"],after:["apply","applyBehavior","after","before"],before:["apply","applyBehavior","after","before"],apply:"apply applyBehavior applyTraits without as after before use".split(" "),applyTraits:"apply applyBehavior applyTraits without as after before use".split(" "),applyBehavior:"apply applyBehavior applyTraits without as after before use".split(" ")},fb={beforeWrap:ka,afterWrap:ka,beforeApply:la,afterApply:la,without:function(a){return{type:a.type,traitList:p(a.traitList),methodNameList:p(a.methodNameList)}},as:function(a){return{type:a.type,trait:a.trait,methodName:a.methodName,methodAlias:a.methodAlias}}},ob={beforeWrap:function(a,c,b){var d=y(b,a.trait);b=b[d];a=a.methodName;c[a]=M((b&&b.proxy||{})[a],c[a],c)},afterWrap:function(a,c,b){var d=y(b,a.trait);b=b[d];a=a.methodName;c[a]=N((b&&b.proxy||{})[a],c[a],c)},beforeApply:function(a,c,b){var d=y(b,a.trait),e=y(b,a.fromTrait),d=b[d],e=b[e];b=d&&d.proxy||{};e=e&&e.proxy||{};a=a.methodName;a in c&&delete c.methodName;c[a]=M(b[a],e[a],c)},afterApply:function(a,c,b){var d=y(b,a.trait),e=y(b,a.fromTrait),d=b[d],e=b[e];b=d&&d.proxy||{};e=e&&e.proxy||{};a=a.methodName;a in c&&delete c.methodName;c[a]=N(b[a],e[a],c)},without:function(a,c,b){var d=a.traitList,e=a.methodNameList.reduce(function(a,b){a[b]=null;return a},{});d.forEach(function(a){a=y(b,a);var d=(a=b[a])&&a.proxy||{};J(d).forEach(function(a){a in e||(a in c&&delete c.methodName,c[a]=function(a,b){return function(){return a.apply(b,arguments)}}(d[a],c))})});a=b=d=e=null},as:function(a,c,b){var d=y(b,a.trait);b=(b=b[d])&&b.proxy||{};d=a.methodAlias;a=a.methodName;d in c&&delete c.methodAlias;c[d]=function(a,b){return function(){return a.apply(b,arguments)}}(b[a],c);a=b=d=b=b=d=a=null}},F=function(a,c){return function(b){return b==c?b:a.call(b).valueOf()}}(K.valueOf,null),E=function(a){return function(c){return a.call(c)}}(K.toString),Z=function(a){return function(c){return a.call(c)}}(nb.toString),pb=function(a){try{a.call(null,"length"),a=function(a,b){return function(c,e){return c!=b&&a.call(c,e)}}(a,null)}catch(c){a=function(a,c){return function(b,d){var e=b!=c;if(e)try{e=a.call(b,d)}catch(m){e=!0}return e}}(a,null)}return a}(K.propertyIsEnumerable),Y=function(a){return function(c){return typeof c==a}}(typeof V),z=function(a){return function(c){return Y(c)&&typeof c.call==a&&typeof c.apply==a}}(typeof V),qb=function(a){return function(c){return t(a).test(E(c))}}(C("Function")),ia=function(a){return function(c){return t(a).test(E(c))}}(C("Object")),rb=z(H.getPrototypeOf)&&H.getPrototypeOf||function(a){var c=a&&a.__proto__;return c||null===c?c:qb(a.constructor)?a.constructor.prototype:a instanceof H?K:null},W=function(a){return function(c){return(c=t(a).exec(Z(c)))&&c[1]}}("^function\\s+([^(]+)\\("),xa=function(a){return function(c){return t(a).test(W(c))}}("^(?:Array|ArrayBuffer|AsyncFunction|Atomics|Boolean|DataView|Date|Error|EvalError|Float32Array|Float64Array|Function|Generator|GeneratorFunction|Int16Array|Int32Array|Int8Array|InternalError|Collator|DateTimeFormat|NumberFormat|Iterator|Map|Number|Object|Promise|Proxy|RangeError|ReferenceError|RegExp|Bool16x8|Bool32x4|Bool64x2|Bool8x16|Float32x4|Float64x2|Int16x8|Int32x4|Int8x16|Uint16x8|Uint32x4|Uint8x16|Set|SharedArrayBuffer|StopIteration|String|Symbol|SyntaxError|TypeError|TypedArray|URIError|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|WeakMap|WeakSet)$"),X=function(a,c){var b=t(c);return b.test(W(a))||b.test(W(rb(a)))},za=function(a){return function(c){return X(c,a)}}("^(?:Node|CharacterData|Event|DOMError|DOMException|DOMImplementation|DOMStringList|DOMTokenList|EventTarget|HTMLCollection|MutationObserver|MutationRecord|NodeFilter|NodeIterator|NodeList|Range|TreeWalker|URL|Document)$"),Aa=function(a){return function(c){return X(c,a)}}("^(?:HTMLElement|HTMLMediaElement|Element)$"),Ba=function(a){return function(c){return X(c,a)}}("^(?:CanvasRenderingContext2D|CanvasGradient|CanvasPattern|TextMetrics|ImageData|DOMStringMap|MediaError|HTMLCollection|NodeList)$"),ta=function(a){return function(c){return t(a).test(E(c))}}(C("GeneratorFunction")),q=function(a){return function(c){return t(a).test(E(c))}}(C("String")),T=z(B.isArray)&&B.isArray||function(a){return function(c){return t(a).test(E(c))}}(C("Array")),sb=z(B.isArguments)&&B.isArguments||function(a,c){var b=function(b){return t(a).test(E(b))};b(arguments)||(b=function(a){return ia(a)&&"number"==typeof a.length&&c(a.length)&&!pb(a,"length")});return b}(C("Arguments"),r.Number.isFinite),p=z(B.from)&&B.from||function(a){return function(c){return a.call(c)}}(B.prototype.slice),ea=function c(b){b=sb(b)&&p(b)||b;T(b)&&(b=b.reduce(function(b,e){return b.concat(c(e))},[]));return b},J=H.keys,u=function(){},D;D=(D=r.console)&&z(D.warn)&&z(D.log)&&D||{warn:u,log:u};L.as=function(c){var b,d,e,f,g;if(G(this))if(f=this.valueOf(),g=f.parentLink,w(g))if(e=g.getSetup(),d=e.chainData,b=d.recentCalleeName,v(b,"as"))if(q(c=F(c)))if(b=f.trait,f=f.methodName,f!==c)d.recentCalleeName="as",ca(e,b,f,c);else throw new h("Using identical method names in case of aliasing is considered to be a rule violating contradiction.");else throw new h("'as(<String>)' excepts as its sole argument just a 'String' type.");else{c=["\u2026 invalid chaining of '",b,"().as()'"].join("");if("applyTraits"==b)throw new n([c," in case of aliasing just a certain behavior."].join(""));throw new n(c);}else throw new k("Please do not spoof the context of 'use'.");else throw new k("Please do not spoof the context of 'apply'.");return g};L.after=function(c){var b,d,e,f,l,m;if(G(this))if(f=this.valueOf(),m=f.parentLink,w(m))if(e=m.getSetup(),d=e.chainData,b=d.recentCalleeName,v(b,"after"))if(x(c))if(b=m.valueOf(),b=b.traitList,0<=b.indexOf(c))if(b=f.trait,b!==c)if(l=f.methodName,f={},c.call(f),g(f[l]))d.recentCalleeName="after",e.useTraits.rules.push(new A({type:"afterApply",trait:c,fromTrait:b,methodName:l}));else throw new k(["Please consider applying '",l,"' directly. This expected behavior has not been implemented by the trait that got passed to 'after'."].join(""));else throw new k("Passing identical trait references to both 'apply' and 'after' is a contradiction that violates the composition rules.");else throw new h("Any trait passed to 'after' has to be registered before via 'use'.");else throw new h("'after(<Trait|Function>)' excepts as its sole argument explicitly either a 'Trait' or a 'Function' type.");else throw new n(["\u2026 invalid chaining of '",b,"().after()'"].join(""));else throw new k("Please do not spoof the context of 'use'.");else throw new k("Please do not spoof the context of 'apply'.");return m};L.before=function(c){var b,d,e,f,l,m;if(G(this))if(f=this.valueOf(),m=f.parentLink,w(m))if(e=m.getSetup(),d=e.chainData,b=d.recentCalleeName,v(b,"before"))if(x(c))if(b=m.valueOf(),b=b.traitList,0<=b.indexOf(c))if(b=f.trait,b!==c)if(l=f.methodName,f={},c.call(f),g(f[l]))d.recentCalleeName="before",e.useTraits.rules.push(new A({type:"beforeApply",trait:c,fromTrait:b,methodName:l}));else throw new k(["Please consider applying '",l,"' directly. This expected behavior has not been implemented by the trait that got passed to 'before'."].join(""));else throw new k("Passing identical trait references to both 'apply' and 'before' is a contradiction that violates the composition rules.");else throw new h("Any trait passed to 'before' has to be registered before via 'use'.");else throw new h("'before(<Trait|Function>)' excepts as its sole argument explicitly either a 'Trait' or a 'Function' type.");else throw new n(["\u2026 invalid chaining of '",b,"().before()'"].join(""));else throw new k("Please do not spoof the context of 'use'.");else throw new k("Please do not spoof the context of 'apply'.");return m};L.apply=ha;sa.without=fa;sa.apply=ha;ra.after=function(c){var b,d,e,f;if(w(this))if(e=this.getSetup(),d=e.chainData,b=d.recentCalleeName,v(b,"after"))if(x(c))if(b=this.valueOf(),b=b.traitList,0<=b.indexOf(c))if(b=this.getChild(),f=b.valueOf(),b=f.trait,b!==c)if(f=f.methodName,b={},c.call(b),g(b[f]))d.recentCalleeName="after",e.useTraits.rules.push(new A({type:"afterWrap",trait:c,methodName:f}));else throw new k(["Please consider applying '",f,"' directly. This expected behavior has not been implemented by the trait that got passed to 'after'."].join(""));else throw new k("Passing identical trait references to both 'apply' and 'after' is a contradiction that violates the composition rules.");else throw new h("Any trait passed to 'after' has to be registered before via 'use'.");else throw new h("'after(<Trait|Function>)' excepts as its sole argument explicitly either a 'Trait' or a 'Function' type.");else throw new n(["\u2026 invalid chaining of '",b,"().after()'"].join(""));else throw new k("Please do not spoof the context of 'use'.");return this};ra.before=function(c){var b,d,e,f;if(w(this))if(e=this.getSetup(),d=e.chainData,b=d.recentCalleeName,v(b,"before"))if(x(c))if(b=this.valueOf(),b=b.traitList,0<=b.indexOf(c))if(b=this.getChild(),f=b.valueOf(),b=f.trait,b!==c)if(f=f.methodName,b={},c.call(b),g(b[f]))d.recentCalleeName="before",e.useTraits.rules.push(new A({type:"beforeWrap",trait:c,methodName:f}));else throw new k(["Please consider applying '",f,"' directly. This expected behavior has not been implemented by the trait that got passed to 'before'."].join(""));else throw new k("Passing identical trait references to both 'apply' and 'before' is a contradiction that violates the composition rules.");else throw new h("Any trait passed to 'before' has to be registered before via 'use'.");else throw new h("'before(<Trait|Function>)' excepts as its sole argument explicitly either a 'Trait' or a 'Function' type.");else throw new n(["\u2026 invalid chaining of '",b,"().before()'"].join(""));else throw new k("Please do not spoof the context of 'use'.");return this};A.prototype.execute=function(c,b){var d=this.valueOf();ob[d.type](d,c,b)};return r.Trait={create:function(c){var b,d,e;g(c=F(c))&&(e={chainData:{isTerminated:!1,recentCalleeName:"use"},useTraits:{root:null,rules:[]},applicator:{body:null,proxy:{},root:{},modifiers:[],requires:[],canRequire:!0,didRequire:!1}},b=e.applicator.root,b.requires=cb.bind(e),b.before=Va.bind(e),b.after=Xa.bind(e),b.before.stateful=Wa.bind(e),b.after.stateful=Ya.bind(e),b.afterReturning=Za.bind(e),b.afterThrowing=$a.bind(e),b.afterFinally=ab.bind(e),b.around=bb.bind(e),b=eb.bind(e),d=db.bind(e),c(b,d),e=mb(e));return e},isTrait:qa,isApplicable:x}})(Function("return this")());</script>



回答4:

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