我正在组织一个小型企业应用程序,但希望尽可能遵循DRY原则。因此,我一直在寻找mixin库。
我发现了这个库,认为它可能是一个不错的选择,因为它允许您在运行时混合和取消混合。此外,我可以只使用一个基类(BaseView),然后从中进行混合。
问题
- 有哪些有用的Mixins实际应用示例?(请勿提供抽象示例)
- 我是否需要扩展类,还是只需使用该库来管理所有扩展和mixin?
我正在组织一个小型企业应用程序,但希望尽可能遵循DRY原则。因此,我一直在寻找mixin库。
我发现了这个库,认为它可能是一个不错的选择,因为它允许您在运行时混合和取消混合。此外,我可以只使用一个基类(BaseView),然后从中进行混合。
问题
如果涉及到JavaScript和基于角色的组合方法,例如Mixins和Traits,我已经有了很明显的观点。我总是会指向两种纯函数模式的库无关组合 - 首先是模块模式,其次是“Flight Mixin”模式,它在2011年5月被重新发现,命名和描述为Angus Croll。但我还建议阅读我在2014年4月发表的一篇论文。
问题
- 1)有哪些有用的Mixins的真实应用示例?(请不要再提供抽象的示例)
- 2)我是否需要扩展类,还是可以使用此库来管理所有扩展和Mixins?
回答你的2个问题...
1)[Observable]
可能是最常见的Mixins真实世界示例之一。但这不是提供其整个代码库的正确位置。从Smart Talents章节中逐渐增长的示例提供了一个[Queue]
工厂的工作实现,它在开始时仅使用不同的Mixins,例如[Enumerable]
和[Allocable]
,但最终还应用了前面提到的[Observable]
。
2)只需使用您选择或需要的模块系统-CommonJS或AMD。因此,您的工厂模块甚至实例/对象通过委派检索附加行为; 因此,它们积极地调用Mixin或Trait模块。
最后-缩短的示例代码:
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; }
var pianist = {
play: function() {}
};
var programmner: {
code: function() {}
};
现在我们可以创建另一个 对象,例如:
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
}
实际上,我并没有完全回答你的问题,但我感觉在这个问题上并没有一个真正的答案。它的实用性取决于你将如何使用它,没有一个“特定应用”的使用场景。
var speakable = {
speak: function(){ return this.name + " speaks" }
}
var flyable = {
fly: function(){ return this.name + " flies" }
}
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工具为对象添加功能,那么你可以自己编写extend 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];
}
}
}
}
与其依赖于像jQuery这样的外部库(这对于这个简单目的是强烈不建议的),或者编写自己的实用程序mixin工具,我们可以利用ES6中可用的Object.assign属性。此外,我们可以在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());