JS 中的组合和 mixin

Posted

技术标签:

【中文标题】JS 中的组合和 mixin【英文标题】:Compostions and mixins in JS 【发布时间】:2017-06-19 08:53:29 【问题描述】:

compositionsmixin 遇到了一些麻烦。 例如,假设我们有一个 AHero 和 Hero1 对象。 所有英雄都可以移动,所以 AHero.move() 是一个东西。 现在,在开发的那一刻,我想添加眩晕和被击晕的可能性。所以我制作了一个看起来像这样的新对象(称为 stunTrait):


stunned : false,
stun(),
beStun,

我决定让英雄们有可能被击晕和被击晕。所以我做了一个 mixin : AHero 类扩展了 stunTrait(超类)。

现在我想让一个被击晕的英雄不能移动。 所以在 Ahero 中:

move()
if(this.stunned) return;
...

后来我发现 stun 是个坏主意,所以我决定停止实施 stunTrait。现在我必须在我的 Ahero 班级中找到所有眩晕的东西,这有时会很困难,如果子班用这些眩晕的东西做一些其他的事情,那就更难了。

有没有办法避免这种情况?更好的设计?


class Object
  constructor(x,y)
    this.x = x
    this.y = y
  


class AHero extends stunTrait(Object)
  constructor(x,y,moveSpeed)
    this.moveSpeed = moveSpeed;
  

  move()
    super.move();
    this.speed = moveSpeed;
  


class hero1 extends AHero
  constructor(x,y,moveSpeed) 


let stunTrait = function(object)
  return class extends object
    constructor(...args) 
      super(...args);

      this.stunned = false;
    

    move()
      if(this.stunned) this.speed = 0;
    
  


let hero = new hero1(40, 50, 100);
hero.stunned = true;
hero.move();
// This work, bcs AHero.move() is called before stunTrait.move()
// but if for some reason, I decide that only hero1 should have stunTrait
// So : class hero1 extends stunTrait(AHero)
// stunTrait.move() will be called after, so stunned will not be applied
// How make compostion without breaking inheritance ?

【问题讨论】:

添加了示例。感谢您的关注! 【参考方案1】:

OP 的给定设计决策旨在修改已经从 class 端实现的 move 行为。

因此stun 行为不仅负责实现其stun 特定的状态处理,它还必须决定 是否和/或如何更改move 行为的控制流。

为此,它不仅依赖于move 的现有实现,还依赖于setSpeed

mixins 的概念确实不适合 OP 的方法,因为它只是将其他行为混合到其他对象中。但是给定的特殊情况也必须处理move 的命名冲突。这就是特质的用途。

如果在 javascript 中曾经真正支持 applicable types,它必须能够同时进行组合特征和对象组合,以及方法修改,一个可能的解决方案可能看起来类似于接下来提供一个。

const withStunBehavior = Trait.create(function (use, applicator) 

  function moveInterceptor(proceedMove, interceptor, argsArray) 
  //console.log("withStunBehavior::moveInterceptor - this.isStunned() : ", this.isStunned());
    if (this.isStunned()) 

      this.setSpeed(0);
    /* else 
      return proceedMove.call(this);
    */
    return proceedMove.call(this);
  

  applicator(function (isStunnedValue) 
    isStunnedValue = !!isStunnedValue;
  //console.log("withStunBehavior::applicator - isStunnedValue : ", isStunnedValue);

    this.beStun = function beStun () 
      isStunnedValue = true;
    ;
    this.unban = function unban () 
      isStunnedValue = false;
    ;
    this.isStunned = function isStunned () 
      return isStunnedValue;
    ;

  ).requires([

    "setSpeed",
    "move"

  ]).around(

    "move", moveInterceptor
  );
);


class AHero 
  constructor(stateValue) 
    var
      aHero = this;

    aHero.getSpeed = function getSpeed () 
      return stateValue.moveSpeed;
    ;
    aHero.setSpeed = function setSpeed (speedValue) 
      return (stateValue.moveSpeed = speedValue);
    ;

    // withStunBehavior.call(aHero);
  
  move() 
    var
      speedValue = this.getSpeed();

    if (speedValue === 0) 
      console.log("Hero does not move at all. Its speed is " + speedValue + ".");
     else 
      console.log("Hero moves with speed " + speedValue + ".");
    
    return speedValue;
  



class AHeroWithStunBehavior extends AHero 

  constructor(stateValue) 
    super(stateValue);

    withStunBehavior.call(this);
  



var
  hero = new AHero( x: 0, y: 0, moveSpeed: 200 );

console.log("\n");
console.log("hero.getSpeed() : ", hero.getSpeed());
console.log("hero.move() : ", hero.move());
console.log("\n");
console.log("hero.setSpeed(0) : ", hero.setSpeed(0));
console.log("hero.getSpeed() : ", hero.getSpeed());
console.log("hero.move() : ", hero.move());
console.log("\n");
console.log("hero.setSpeed(100) : ", hero.setSpeed(100));
console.log("hero.getSpeed() : ", hero.getSpeed());
console.log("hero.move() : ", hero.move());
console.log("\n");
console.log("hero.isStunned : ", hero.isStunned);
console.log("hero.beStun : ", hero.beStun);
console.log("hero.unban : ", hero.unban);

var
  stunningHero = new AHeroWithStunBehavior( x: 40, y: 50, moveSpeed: 130 );

console.log("\n");
console.log("stunningHero.isStunned : ", stunningHero.isStunned);
console.log("stunningHero.beStun : ", stunningHero.beStun);
console.log("stunningHero.unban : ", stunningHero.unban);
console.log("\n");
console.log("stunningHero.getSpeed() : ", stunningHero.getSpeed());
console.log("stunningHero.move() : ", stunningHero.move());
console.log("\n");
console.log("stunningHero.setSpeed(0) : ", stunningHero.setSpeed(0));
console.log("stunningHero.getSpeed() : ", stunningHero.getSpeed());
console.log("stunningHero.move() : ", stunningHero.move());
console.log("\n");
console.log("stunningHero.setSpeed(90) : ", stunningHero.setSpeed(90));
console.log("stunningHero.getSpeed() : ", stunningHero.getSpeed());
console.log("stunningHero.move() : ", stunningHero.move());
console.log("\n");
console.log("stunningHero.beStun() : ", stunningHero.beStun());
console.log("stunningHero.move() : ", stunningHero.move());
console.log("\n");
console.log("stunningHero.setSpeed(160) : ", stunningHero.setSpeed(160));
console.log("stunningHero.getSpeed() : ", stunningHero.getSpeed());
console.log("stunningHero.move() : ", stunningHero.move());
console.log("\n");
console.log("stunningHero.unban() : ", stunningHero.unban());
console.log("stunningHero.move() : ", stunningHero.move());
console.log("\n");
console.log("stunningHero.setSpeed(160) : ", stunningHero.setSpeed(160));
console.log("stunningHero.getSpeed() : ", stunningHero.getSpeed());
console.log("stunningHero.move() : ", stunningHero.move());
.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===aafunction 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 efunction 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 ffunction Ga(a,c,b,d)return function()var e=arguments,f=a.apply(b,e);c.call(b,f,e,d);return ffunction Ha(a,c,b,d)return function()var e=arguments,f;tryf=a.apply(b,e)catch(l)c.call(b,l,e,d)return ffunction Ia(a,c,b,d)return function()var e=arguments,f,g;tryf=a.apply(b,e)catch(m)g=mc.call(b,g||f,e,d);return ffunction 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.");elsea=["\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 efunction 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 bfunction 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 bfunction 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 afunction 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()returntrait:a.trait,methodName:a.methodName,parentLink:a.parentLink;this.toString=function()return["ApplyLink::singleBehavior :: ",S.stringify(a)].join("");return thisfunction 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()returntraitList:a.traitList,parentLink:a.parentLink;this.toString=function()return["ApplyLink::fromTraits :: ",S.stringify(a)].join("");return thisfunction 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()returntraitList: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 thisfunction 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 dfunction 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 dfunction 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 dfunction 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 dfunction 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 dfunction $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 dfunction 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 dfunction 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 dfunction 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.");elseif(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 cfunction 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.rootfunction 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=afunction y(a,c)return a.findIndex(function(a)return a.trait===c)function ka(a)returntype:a.type,trait:a.trait,methodName:a.methodNamefunction la(a)returntype:a.type,trait:a.trait,fromTrait:a.fromTrait,methodName:a.methodNamefunction 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).lengthreturn cfunction 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 dfunction 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 thisfunction jb(a)this.requires=function()return p(a);return thisfunction 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 thisfunction 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 thisfunction 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 cfunction 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)returntrait: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 cvar 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)returntype:a.type,traitList:p(a.traitList),methodNameList:p(a.methodNameList),as:function(a)returntype: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)trya.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)trye=a.call(b,d)catch(m)e=!0return 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.");elsec=["\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>

【讨论】:

这是一个了不起的答案!非常感谢 Trait 对于大型应用程序来说似乎很棒,为什么它们没有更受欢迎? github.com/traitsjs/traits.js 1/2 @hildericsb >> ... 为什么它们没有更受欢迎一点? 2/2 @hildericsb ... ES3 语言核心确实提供了。因此,大多数人没有掌握其他语言的能力和/或没有感到压力足以要求关于如何管理 JS 代码库的新(或切换)观点,它不是(甚至不是)新的最佳启动平台想法。

以上是关于JS 中的组合和 mixin的主要内容,如果未能解决你的问题,请参考以下文章

无法从 Vue.js 中的单个文件组件导入 Mixin

vue.js 中的mixins 的用法

vue.js 中的mixins 的用法

TypeScript 中的 Mixin

在打字稿中提取道具以分离Vue组合API中的模块

Vue js:如何在两个组件中使用 mixin 功能?通过执行错误