最近,我发现了两篇关于mixins的文章。这让我对哪一个比其他人更好感到困惑。
来自mdn
的第一个
var calculatorMixin = Base => class extends Base {
calc() { }
};
var randomizerMixin = Base => class extends Base {
randomize() { }
};
class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }
来自https://javascript.info/mixins的第二个
let sayMixin = {
say(phrase) {
alert(phrase);
}
};
let sayHiMixin = {
__proto__: sayMixin, // (or we could use Object.create to set the prototype here)
sayHi() {
// call parent method
super.say(`Hello ${this.name}`);
},
sayBye() {
super.say(`Bye ${this.name}`);
}
};
class User {
constructor(name) {
this.name = name;
}
}
// copy the methods
Object.assign(User.prototype, sayHiMixin);
// now User can say hi
new User("Dude").sayHi(); // Hello Dude!
在这些场景中创建的对象也具有不同的组成/结构。
现在我很困惑,哪一个比其他人好。
提供的优势比另一方更具优势。
因此我应该选择使用哪一种。
答案 0 :(得分:2)
一种简单的方法:
// mixin
let sayHiMixin = {
sayHi() {
alert(`Hello ${this.name}`);
},
sayBye() {
alert(`Bye ${this.name}`);
}
};
// usage:
class User {
constructor(name) {
this.name = name;
}
}
// copy the methods
Object.assign(User.prototype, sayHiMixin);
// now User can say hi
new User("Dude!").sayHi(); // Hello Dude!
答案 1 :(得分:1)
我完全赞同贾里德史密斯。与任何工具或工具集一样,需要知道是否要使用它。如果出于任何原因选择了 mixins 的概念,那么应该真正知道它的功能和错过的内容,特别是如果应用于JavaScript的编程范例/概念。
在那里我很自以为是,因此我将从我自己的观点提供以下想法和技术方法。其他解决方案要广泛得多,其中一些我有时也会使用。
让我们选择OP提供的第一个来源。例如,如果一个人将上面给出的例子重写为......
const calculatorMixin = Base => class extends Base {
calc() { }
};
const randomizerMixin = Base => class extends Base {
randomize() { }
};
class Baz { }
const calcAndRandomizeMixin = calculatorMixin(randomizerMixin(Baz));
class Biz extends calcAndRandomizeMixin { }
const biz = new Biz;
console.log('(biz instanceof Biz) ? ', (biz instanceof Biz));
console.log('(biz instanceof Baz) ? ', (biz instanceof Baz));
console.log('(biz instanceof calcAndRandomizeMixin) ? ', (biz instanceof calcAndRandomizeMixin));

.as-console-wrapper { max-height: 100%!important; top: 0; }

...然后我最关心的问题是它的方法本身完全基于类及其扩展。 " mixins" 是类,由类工厂立即创建。他们总是扩展另一个班级。因此它是纯粹的继承。
即使有人正在编写这样的 mixin-class 作为行为的容器,&#34;可以做&#34; 的事情,以及后来的类型< em>&#34;具有&#34; 某些行为,这种方法在技术上根本不承认mixins背后的概念,因为它本质上是基于一个孩子 - 父母或一个&#34;是&#34; 的关系。
第二种方法,即使用对象和Object.assign
,乍一看看起来像是许多古老混合方法的现代变体,当时所有方法都使用了与对象相关联的行为和自编写的{{ {1}}方法......就像... extends
。
这种方法的独特之处在于它如何支持/解决从其他mixin创建的&#34; composite-mixins&#34; ... mixins。在我看来,通过extends(targetObject, mixinSourceObject)
委派链接行为并将另一个基于对象的mixin分配给mixin的super
属性是可行且优雅的。
我个人会花更多的时间在第二次提供的方法上进行研究。
还有另一种方法......基于功能的mixins。第二个基于对象的mixin-example重写成基于函数的挂件的代码看起来像这样......
__proto__
&#13;
const sayMixin = (function () { // basic function-based mixin.
// shared code. //
function say(phrase) { // a single implementation
console.log(phrase); // of `say` behavior ...
} //
// return function based mixin. //
return function sayMixin () { // ... that might get applied
this.say = say; // many times but always as
}; // reference / shared code.
}());
const sayHiMixin = (function () { // function-based *composite-mixin*.
// shared code.
// object that helps with behavior forwarding.
const sayProxy = {};
// apply behavior of `sayMixin`.
sayMixin.call(sayProxy);
// a single implementation of `sayHi` behavior.
function sayHi() {
sayProxy.say(`Hello ${this.name}!`); // forwarding.
}
// a single implementation of `sayBye` behavior.
function sayBye() {
sayProxy.say(`Bye ${this.name}!`); // forwarding.
}
// return function based composite mixin.
return function sayHiMixin () {
this.sayHi = sayHi; // - always shares one and the ...
this.sayBye = sayBye; // ... same implementation(s).
};
}());
class User {
constructor(name) {
// public property.
this.name = name;
}
}
// apply the composite `sayHiMixin`.
sayHiMixin.call(User.prototype);
// now a `User` can say hi and bye
const dude = new User('Dude');
dude.sayHi(); // Hello Dude!
dude.sayBye(); // Bye Dude!
console.log('dude.name : ', dude.name); // Dude
&#13;
即使现在选择这种方法,也有一些很好的论据。首先,它与OP提到的其他两个共同点......不需要额外的库。其次,它确实在每个给定的ES3环境中运行,与其他环境不同。第三,基于函数的方法将mixins实现为&#34;适用的类型&#34;,因此可以免费获得委托和封装。
现在将证明两者的力量。仍在使用第二个示例代码和引入的基于函数的mixin方法,可以非常轻松地创建另一个用户将其隐藏起来.as-console-wrapper { max-height: 100%!important; top: 0; }
来自公众,但确实通过它公开它name
行为。当然,以下代码仅用于理解概念。在实践中很难实现一种&#34;混合复合混合物&#34; 就像那样......
say
&#13;
const sayMixin = (function () { // basic function-based mixin.
// shared code. //
function say(phrase) { // a single implementation
console.log(phrase); // of `say` behavior ...
} //
// return function based mixin. //
return function sayMixin () { // ... that might get applied
this.say = say; // many times but always as
}; // reference / shared code.
}());
const sayHiMixin = (function () { // function-based *composite-mixin*.
// shared code.
// object that helps with behavior forwarding.
const sayProxy = {};
// apply behavior of `sayMixin`.
sayMixin.call(sayProxy);
// a single implementation of `sayHi` behavior.
function sayHi() {
sayProxy.say(`Hello ${this.name}!`); // forwarding.
}
// a single implementation of `sayBye` behavior.
function sayBye() {
sayProxy.say(`Bye ${this.name}!`); // forwarding.
}
// // return function based composite mixin.
// return function sayHiMixin () {
// this.sayHi = sayHi; // - always shares one and the ...
// this.sayBye = sayBye; // ... same implementation(s).
// };
// return function based hybrid composite mixin.
return function sayHiMixin (properties) {
if (properties && (typeof properties === 'object')) {
console.log('sayHiMixin :: payload bound to behavior');
this.sayHi = sayHi.bind(properties); // - creates each a ...
this.sayBye = sayBye.bind(properties); // ... new reference.
} else {
console.log('sayHiMixin :: direct behavior reference');
this.sayHi = sayHi; // - always shares one and the ...
this.sayBye = sayBye; // ... same implementation(s).
}
};
}());
class User {
constructor(name) {
// public property.
this.name = name;
}
}
// apply the composite `sayHiMixin`.
sayHiMixin.call(User.prototype);
// now a `User` can say hi and bye
const dude = new User('Dude');
dude.sayHi(); // Hello Dude!
dude.sayBye(); // Bye Dude!
console.log('dude.name : ', dude.name); // Dude
class AnotherUser {
constructor(name) {
// local property + public accessor methods.
sayHiMixin.call(this, { name: name });
}
}
// now a `User` can say hi and bye
const john = new AnotherUser('John');
john.sayHi(); // Hello John!
john.sayBye(); // Bye John!
console.log('john.name : ', john.name); // undefined
&#13;
基于功能的mixins的意见摘要
正如ES3提供的功能已经提供,通过闭包封装,明确授权功能以及通过.as-console-wrapper { max-height: 100%!important; top: 0; }
/ call
应用不同的上下文,已经可以从基于mixin的组合开始。结合这些技术可以实现更强大的概念,如冲突解决,可以/将基于已经通过代理参考和一些功能组合演示的转发。也可以注入和传递附加状态。因此,人们甚至可以实现超越mixin的概念,例如Traits,Stateful Traits和Talents,后者是真正符合JavaScript语言范例的组合概念。
是否使用mixin的经验法则
只有在代码重用可以用 observable 等形容词来描述和/或在整个系统中,不相似的类和/或异构类型需要相同的额外行为。