我正在组织一个小型企业应用程序,但希望尽可能保持干燥。因此,我一直在研究 mixin 库。
我遇到了这个库,并认为它可能是一个不错的选择,因为它允许您在运行时混入和混出。另外,例如,我可以只有一个基类 (BaseView),然后将其混合。
问题
- 有用的 Mixin 有哪些真实的应用示例?(请不要再抽象的例子)
- 我什至需要扩展类还是可以只使用这个库来管理所有扩展和混合?
我正在组织一个小型企业应用程序,但希望尽可能保持干燥。因此,我一直在研究 mixin 库。
我遇到了这个库,并认为它可能是一个不错的选择,因为它允许您在运行时混入和混出。另外,例如,我可以只有一个基类 (BaseView),然后将其混合。
问题
一个混入仅仅是一个不同的概念想法如何组织代码和继承。您当然可以将它与使用经典或原型继承结合起来,但它也可以独立工作,可以这么说。
例如,我们将真正从多个其他对象“形成”新的独立对象,而不是创建“委托”对象属性/查找(如原型继承)。这有时也称为“多重继承”,单靠Javascript原型继承无法轻松实现。
举个例子:
var pianist = {
play: function() {}
};
var programmner: {
code: function() {}
};
现在我们可以创建另一个Object,比如
var Jim = Object.create( null ); // create a fully self-defining object
extend( Jim, pianist );
extend( Jim, programmer );
这个伪extend
方法看起来像(ES5):
function extend( target, source ) {
Object.getOwnPropertyNames( source ).forEach(function( key ) {
Object.defineProperty( target, key, Object.getOwnPropertyDescriptor(source, key)) });
return target
}
我实际上没有正确回答你的问题,但我觉得你的问题没有真正的答案。它与您将要使用的一样真实,实际上没有“特定于应用程序”的用例。
另请参阅:
如果涉及到 JavaScript 和基于角色的组合方法,如 Mixins 和 Traits,我同时非常固执己见。我总是会指出 2 个纯函数模式的库不可知组合 - 首先是module模式,其次是“Flight Mixin”模式,因为它已被 Angus Croll 在 2011 年 5 月重新发现、命名和描述。但我也建议阅读我 2014 年 4 月的一篇论文。
问题
- 1) 有用的 Mixin 有哪些真实的应用示例?(请不要再抽象的例子)
- 2)我什至需要扩展类还是我可以使用这个库来管理所有扩展和混合?
回答你的2个问题...
1st)[Observable]
可能是Mixins最常见的现实世界示例之一。但这不是提供其整个代码库的正确位置。从本次生长的例子智能人才章不提供工作实现[Queue]
工厂,在刚开始使用不同的混入像[Enumerable]
和[Allocable]
,但最后也适用于已经提到[Observable]
。
2) 只需使用您选择或需要的module系统 - CommonJS 或 AMD。你的工厂module甚至实例/对象然后通过委托检索额外的行为;因此,它们积极地做call
/apply
的密新或性状module。
最后 - 缩短示例代码:
var Observable_SignalsAndSlots = (function () {
var
Event = function (target, type) {
this.target = target;
this.type = type;
},
EventListener = function (target, type, handler) {
var defaultEvent = new Event(target, type);
this.handleEvent = function (evt) {
/* ... */
};
this.getType = function () {
return type;
};
this.getHandler = function () {
return handler;
};
},
EventTargetMixin = function () {
var eventMap = {};
this.addEventListener = function (type, handler) {
/* ... */
};
this.dispatchEvent = function (evt) {
/* ... */
};
}
;
return EventTargetMixin;
}).call(null);
var Queue = (function () {
var
global = this,
Observable = global.Observable_SignalsAndSlots,
//Allocable = global.Allocable,
Queue,
onEnqueue = function (queue, type) {
queue.dispatchEvent({type: "enqueue", item: type});
},
onDequeue = function (queue, type) {
queue.dispatchEvent({type: "dequeue", item: type});
},
onEmpty = function (queue) {
queue.dispatchEvent("empty");
}
;
Queue = function () { // implementing the [Queue] Constructor.
var
queue = this,
list = []
;
queue.enqueue = function (type) {
list.push(type);
onEnqueue(queue, type);
return type;
};
queue.dequeue = function () {
var type = list.shift();
onDequeue(queue, type);
(list.length || onEmpty(queue));
return type;
};
Observable.call(queue);
//Allocable.call(queue, list);
};
return Queue;
}).call(null);
var q = new Queue;
q.addEventListener("enqueue", function (evt) {console.log("enqueue", evt);});
q.addEventListener("dequeue", function (evt) {console.log("dequeue", evt);});
q.addEventListener("empty", function (evt) {console.log("empty", evt);});
console.log("q.addEventListener : ", q.addEventListener);
console.log("q.dispatchEvent : ", q.dispatchEvent);
console.log("q.enqueue('the') ... ", q.enqueue('the')); // "enqueue" Object {type: "enqueue", item: "the", target: Queue}
console.log("q.enqueue('quick') ... ", q.enqueue('quick')); // "enqueue" Object {type: "enqueue", item: "quick", target: Queue}
console.log("q.enqueue('brown') ... ", q.enqueue('brown')); // "enqueue" Object {type: "enqueue", item: "brown", target: Queue}
console.log("q.enqueue('fox') ... ", q.enqueue('fox')); // "enqueue" Object {type: "enqueue", item: "fox", target: Queue}
console.log("q.dequeue() ... ", q.dequeue()); // "dequeue" Object {type: "dequeue", item: "the", target: Queue}
console.log("q.dequeue() ... ", q.dequeue()); // "dequeue" Object {type: "dequeue", item: "quick", target: Queue}
console.log("q.dequeue() ... ", q.dequeue()); // "dequeue" Object {type: "dequeue", item: "brown", target: Queue}
console.log("q.dequeue() ... ", q.dequeue()); // "dequeue" Object {type: "dequeue", item: "fox", target: Queue}
// "empty" Object {target: Queue, type: "empty"}
console.log("q.dequeue() ... ", q.dequeue()); // "dequeue" Object {type: "dequeue", item: undefined, target: Queue}
// "empty" Object {target: Queue, type: "empty"}
.as-console-wrapper { max-height: 100%!important; top: 0; }
我们使用一个名为Cocktail的 mixin 库(mixins...明白了吗?)。它专门用于 Backbone 应用程序,但非常好。
我们已经编写了有关我们的使用模式的详细信息,这些详细信息比我在这里描述的要好。
在面向对象的设计模式中,目标是尽可能地消除继承,因为继承是不灵活的并且将不相关的实体混杂在一起。您应该针对接口进行编程,而不是针对实现进行编程。将不同的与保持不变的分开。设计模式以策略模式的形式引入了委托的概念。但是由于 JavaScript 的动态特性,我们也可以很容易地使用 mixin 的概念来区分不同的和保持不变的。
另一个选择 mixins 的原因是因为 JavaScript 是一种原型语言,它通过原型链将对象链接在一起,但是如果你过度使用这个特性,你会发现在巨大的链中链接原型很慢。
事实上,通过使用 mixins,您可以直接向对象的原型添加功能,而不是链接原型,从而降低查找算法的开销。因此,例如,下面我们创建了两个单例对象“speak”和“fly”。这些是实现行为的对象。不同的东西会说话会飞,但那些不同的东西不应该相互继承。例如,您可能有一只野鸭和一只鹦鹉。
var speakable = {
speak: function(){ return this.name + " speaks" }
}
var flyable = {
fly: function(){ return this.name + " flies" }
}
在 jQuery 世界中以及在 ES6 的干净类语法之前,您将使用构造函数按如下方式添加 mixins:
var MallardDuck = function(name){
this.name = name;
}
var Parrot = function(name){
this.name = name;
}
$.extend(MallardDuck.prototype, speakable, flyable);
$.extend(Parrot.prototype, speakable, flyable);
var duck = new MallardDuck('Bob');
var parrot = new Parrot('Jane');
console.log(duck.speak());
console.log(parrot.speak());
在没有 jQuery 和 ES6 之前,如果您想通过 mixin 工具扩展具有功能的对象,那么您可以简单地编写自己的扩展 mixin 工具:
function extend(target){
// if no mixin objects are provided, then return out of function
if(!arguments[1])
return;
// the first argument is the target object we want to mix behavior into, so we start our loop at index 1
for(var i = 1; j = arguments.length; i < j; i++) {
// grab the singleton object to mixin into target
var source = arguments[i];
for(var propr in source) {
// ensure we do not override a property that target already has
if(!target[prop] && source.hasOwnProperty(prop)){
target[prop] = source[prop];
}
}
}
}
我们可以利用 ES6 中可用的 Object.assign 属性,而不是依赖像 jQuery 这样的外部库(出于这个简单的目的,强烈建议不要使用这种库),或者编写我们自己的实用程序混合工具。此外,我们可以在 ES6 类中使用它。Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
const speakable = {
speak: function(){ return `${this.name} speaks`; }
}
const flyable = {
fly: function(){ return `${this.name} flies` }
}
class MallardDuck {
constructor(name) {
this.name = name;
Object.assign(MallardDuck.prototype, speakable); // add the mixin to prototype chain. Alternatively, use 'this' to add directly to the new object being created
}
}
class Parrot {
constructor(name) {
this.name = name;
Object.assign(Parrot.prototype, speakable); // add the mixin to prototype chain. Alternatively, use 'this' to add directly to the new object being created
}
}
const duck = new MallardDuck('Mallard Duck');
const parrot = new Parrot('Parrot');
console.log(duck.speak());
console.log(parrot.speak());