使用模块模式时Javascript混合
我使用模块模式已经有一段时间了,但最近我开始希望将函数和属性混入其中,以增加代码的重用。我已经阅读了一些关于这个主题的好资料,但仍然有点不确定最佳方法。以下是一个模块:使用模块模式时Javascript混合,javascript,design-patterns,mixins,module-pattern,Javascript,Design Patterns,Mixins,Module Pattern,我使用模块模式已经有一段时间了,但最近我开始希望将函数和属性混入其中,以增加代码的重用。我已经阅读了一些关于这个主题的好资料,但仍然有点不确定最佳方法。以下是一个模块: var myModule = function () { var privateConfigVar = "Private!"; //"constructor" function module() {} module.publicMethod = function () { co
var myModule = function () {
var privateConfigVar = "Private!";
//"constructor"
function module() {}
module.publicMethod = function () {
console.log('public');
}
function privateMethod1() {
console.log('private');
}
return module;
}
这是一个mixin对象:
var myMixin = function () {};
Mixin.prototype = {
mixinMethod1: function () {
console.log('mixin private 1');
},
mixinMethod2: function () {
console.log('mixin private 2');
}
};
理想情况下,我希望将来自其他对象的一些方法作为私有方法和一些作为公共方法混合在一起,这样我就可以调用一些扩展函数,参数为private/public。那么
mixin(myModule, myMixin, "private");
通过仅调用mixinMethod1,使myMixin方法在myModule中可用,并具有正确的作用域,以及:
mixin(myModule, myMixin, "public");
通过调用module.mixinMethod1使myMixin方法在myModule中可用,并具有正确的作用域
我尝试过使用一种方法将属性从一个原型复制到另一个原型,我尝试过下划线扩展方法将对象的属性从一个复制到另一个,以及在两者之间的各种事情。在这一点上,我认为我对范围和原型有点掉转了,我希望在使用模块模式时,能有一些关于如何最好地进行这样的混合的指导。请注意,无论是向原型添加函数还是模块本身,myMixin对象的外观都无关紧要,我只是想找出一些方法使其工作
谢谢!
因此,[some code]通过调用mixinMethod1使myMixin方法在myModule中可用,并具有正确的作用域
那是不可能的。不能通过调用函数来修改作用域,尤其是不能从外部调用。另请参见,了解其设计原因
那么,你能做什么
从模块外部
模块函数的私有作用域没有任何内容。显然,您不能使用模块的私有函数。您可以使用最常见的方法扩展其原型,甚至可以。在这些函数中,您可以使用自己的私有函数,可以是完全静态的函数,也可以是特定于类的函数
var myMixin = (function() {
// everything class-unspecific but mixin-local
var staticMixinVariables, …;
function globalPrivateFunction(){…}
function staticMethod(){…}
return function(mod) {
// everything class-specific
// also using the locals from above
mod.staticHelper = function() { staticMixinVariable … };
mod.prototype.mixinMethod1 = staticMethod;
mod.prototype.mixinMethod2 = function(){…};
…
};
})();
// Example:
myMixin(SomeClass)
从模块内部
在模块代码本身中使用mixin可以允许更大的灵活性
var myMixin = (function() {
// everything class-unspecific but mixin-local
…
return {
publicHelper1: function(){…},
publicHelper2: function(){…},
decorateInstance: function(o) {
o.xy = …;
},
extendPrototype: function(proto) {
// everything class-specific
// also using the locals from above
proto.mixinMethod1 = staticMethod;
proto.mixinMethod2 = function(){…};
…
}
};
})();
有了这样一个接口,就可以很容易地构造一个将其用作mixin而不是从中继承的类:
var myClass = (function() {
function Constructor() {
myMixin.decorateInstance(this);
…
}
Constructor.prototype.method1 = function() { myMixin.publicHelper1() … };
Constructor.prototype.method2 = function() { … };
myMixin.extendPrototype(Constructor.prototype);
Constructor.myHelper = myMixin.publicHelper2; // re-export explicitly
return Constructor;
})();
但是,mixin永远无法访问私有类变量,也不能提供私有的、特定于类的API。尽管如此,我们仍然可以使用依赖项注入显式地提供该访问,并使mixin工厂生效:
var myClass = (function() {
var … // private class functions and variables
var mixer = myMixin(privateClassHelper,
privateClassVariable,
function setPrivateVar(x) {…},
… );
var myHelper = mixer.customHelper, … // local "aliases"
function Constructor(localX) {
mixer.decorateInstance(this, localX);
…
}
… // further using the class-specific private mixer
return Constructor;
})();
并非以上所示的所有技术都需要在每个mixin中使用,只需选择您需要的技术即可。上面的示例中并没有显示所有可能的技术,而且:-mixin模式可以应用于普通模块或在其声明中,上面的示例只显示了带有原型的类
对于一些好的例子,以及无状态特征、有状态混合和它们的特权对应之间的理论区别,请看一看。with关键字可以非常有用地定义范围,但它也有一些缺点,在严格模式下是禁止的
使用with关键字,可以在模块主体内定义私有变量privateScope,该变量将包含所有provate方法:
var myModule = function () {
var privateConfigVar = "Private!";
var privateScope = {};
//"constructor"
function module() {}
var proto = module.prototype;//avoids multiple attribute lookup
//Let's re-define you example' private method, but with a new strategy
privateScope['privateMethod1'] = function() {
console.log('private');
}
proto.publicMethod = function () {
with(privateScope){
//this call should work
privateMethod1();
}
console.log('public');
}
proto.publicMethod2=function(name,fn){
with(privateScope){
//this will be defined later by a Mixin
otherPrivateMethod();
}
console.log('public2');
}
proto.definePrivateFunction=function(name,fn){
privateScope[name] = fn;
}
return module;
}
您的mixin将使用刚才定义的definePrivate函数将私有方法添加到私有范围:
//An example mixin implementation
function Mixin(source,target,flag){
if(flag==="private"){
for(var currentMethodName in source){
target.definePrivateFunction(currentMethodName,source[currentMethod])
}
}else{
for(var currentMethodName in source){
target[currentMethodName]=source[currentMethod];
}
}
}
以下代码应该可以正常工作:
var test = myModule();
var testInstance = new test();
testInstance.publicMethod();// will call the private method defined internally
Mixin({
otherPrivateMethod:function(){
console.log("other Prvate Method called")
}
},test.prototype,"private");
testInstance.publicMethod2();// will call the private method defined by the mixin
理想情况下,我希望将来自其他对象的一些方法作为私有方法和一些作为公共方法混合在一起,这样我就可以调用一些扩展函数,参数为private/public
正如已经提到的那样,没有办法完全实现这一目标
所以,那。。。只需调用mixinMethod1即可使myMixin方法在myModule中可用,并且具有正确的作用域,并且:。。。通过调用module.mixinMethod1使myMixin方法在myModule中可用,并具有正确的作用域
关于范围。。。这是由函数创建的闭合地址空间。
除了闭包,作用域仅在函数运行时可用
在这个函数的主体中。它永远不可能被操纵/欺骗
人们所寻找的术语是上下文。JavaScript,在许多方面都是高度
动态的,是建立在对象/目标/上下文的后期绑定之上的方法
在运行时对上进行计算/查找,然后。
上下文可以通过遍历原型链自动委托
或者通过每个函数对象都提供的两种调用方法之一显式执行
-要么打电话,要么申请
因此,已经处于语言核心级别的JavaScript确实提供了基于函数的
Mixin模式比任何可用的extends或Mixin模式都强大
它的实现免费提供委托,并且能够传递
几乎每一个被指责的帮手都缺少的州,除非有
再次以一种相当迂回的方式实现此功能的努力
直截了当地说,就是屁股向后。
因为他已经赢得了赏金。
在他回答的最后一段中,有一个与我的资源相关的链接
在发表推荐演讲3个月后已经过时了。由于没有
名誉点数够多了,我不能直接评论他的回答。为此
现在我将借此机会指出我个人研究和实践的最新情况
理解
再次回答OP的问题
我将从假定的模块模式中更改前两个给定的代码示例
并且相当典型地提供了面向普通构造函数的mixin代码库
同时,我很想称之为代理和/或双上下文混合
总结一下同时委托两个不同的目标/上下文对象的机制。
因此,演示了一个纯粹的基于函数的mixin模式,它可能最接近于
OP试图实现这一目标
var MyBicontextualMixin = function (localProxy) {
localProxy.proxifiedAccessible = function () {
console.log("proxified accessible.");
};
this.publiclyAccessible = function () {
console.log("publicly accessible.");
};
};
var MyConstructor = function () {
var localProxy = {};
MyBicontextualMixin.call(this, localProxy);
var locallyAccessible = localProxy.proxifiedAccessible;
// call 'em
locallyAccessible(); // "proxified accessible."
this.publiclyAccessible(); // "publicly accessible."
};
(new MyConstructor);
// will log:
//
// proxified accessible.
// publicly accessible.
这种特殊的模式也是构成纯音乐的基础
依赖于冲突解决功能的基于功能的特征
由不会公开此功能的代理mixin提供
公开
因为没有在理论上结束,所以会有一个现实世界的例子,
用各种可重用的混合模块组成一个队列模块
崇拜干燥的方法。它还应该回答OP关于
如何在模块的基础上实现封装和展示
基于模式和函数的混合合成
变量可枚举\u第一个\u最后一个\u项=函数全局{
变量
parseFloat=global.parseFloat,
math_floor=global.math.floor,
//共享代码。
第一=功能{
返回此[0];
},
last=函数{
返回此[this.length-1];
},
项目=功能idx{
返回这个[math_floorparseFloatidx,10];
}
;
返回函数{/[Enumerable_first_last_item]Mixin。
var enumerable=此;
enumerable.first=first;
enumerable.last=last;
enumerable.item=项目;
};
}窗户| |这个;
变量可枚举\u第一个\u最后一个\u项目\u代理=函数列表{
可枚举的\u first\u last\u item.calllist;
//实现代理/双上下文[Enumerable_first_last_item]混合。
var enumerable=此;
enumerable.first=函数{
返回列表。首先;
};
enumerable.last=函数{
返回列表.last;
};
enumerable.item=函数idx{
return list.itemidx;
};
};
var Allocable=函数数组{
变量
array|from=typeof array.from==function&&array.from | | function array_prototype_切片{
返回函数列表类型{
返回数组\u prototype\u slice.calllistType;
};
}Array.prototype.slice
;
返回函数列表{//proxified/bicontextual[Allocable]Mixin。
变量
可分配的
;
allocable.valueOf=allocable.toArray=函数{
从列表返回数组_;
};
allocable.toString=函数{
返回+列表;
};
allocable.size=函数{
返回列表长度;
};
可枚举的\u第一个\u最后一个\u项目\u proxified.callallocable,列表;
};
}阵列;
var Queue=函数{//[Queue]模块。
变量
onEnqueue=函数队列,类型{
//dispatchEvent{type:enqueue,item:type};
},
onDequeue=函数队列,类型{
//dispatchEvent{type:dequeue,item:type};
}/*,
onEmpty=函数队列{
//queue.dispatchEvent{type:empty};
}*/,
onEmpty=函数队列{
//queue.dispatchEventempty;
},
Queue=函数{//[Queue]构造函数。
变量
队列=此,
列表=[]
;
queue.enqueue=函数类型{
list.pushtype;
队列,类型;
返回类型;
};
queue.dequeue=函数{
var type=list.shift;
查询队列,类型;
list.length | | onEmptyqueue;
返回类型;
};
//Observable.callqueue;//应用[Observable]Mixin。
Allocable.callqueue,list;//应用双上下文[Allocable]Mixin。
},
isQueue=函数类型{
return!!type&&type instanceof Queue;
},
createQueue=函数{//[Queue]工厂。
返回新队列;
}
;
返回{//[Queue]模块。
isQueue:isQueue,
创建:createQueue
};
};
var q=Queue.create;
//q、 AddEventListenerQueue,函数evt{/*…*/};
//q、 addEventListenerdequeue,函数evt{/*…*/};
//q、 addEventListenerempty,函数evt{/*…*/};
console.logq:,q;//{ .., .., .., }
console.logq.size:,q.size;//0
console.logq.valueOf:,q.valueOf;//[]
敏捷的棕色狐狸跳过了懒狗。split/\s+/.forEachfunction elm/*,idx,arr*/{
console.logq.enqueue\+elm+,q.enqueueelm;
};
console.logq.size:,q.size;//9
console.logq.toArray:,q.toArray;//[ .., .., .., ]
console.logq.first:,q.first;//这个
console.logq.last:,q.last;//狗
console.logq.item2
:,q.item2;//棕色的
console.logq.item5:,q.item5;//结束
console.logq.dequeue,q.dequeue;//这个
console.logq.dequeue,q.dequeue;//快的
console.logq.dequeue,q.dequeue;//棕色的
console.logq.dequeue,q.dequeue;//狐
console.logq.dequeue,q.dequeue;//跳
console.logq.size:,q.size;//4.
console.logq.toArray:,q.toArray;//[ .., .., .., ]
console.logq.first:,q.first;//结束
console.logq.last:,q.last;//狗
console.logq.item2:,q.item2;//懒惰的
console.logq.item5:,q.item5;//未定义
.作为控制台包装{最大高度:100%!重要;顶部:0;}