我使用模块模式已经有一段时间了,但最近开始想将函数和属性混合到其中,以提高代码的重用。我已经阅读了一些关于这个主题的很好的资源,但是对于最好的方法仍然有点不确定。下面是一个模块:
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()并具有正确的作用域,即可在myModule中使用myMixin方法,并且:
mixin(myModule, myMixin, "public");通过调用module.mixinMethod1()使myMixin方法在myModule中可用,并具有正确的作用域
我尝试过使用将属性从一个原型复制到另一个原型的方法,我尝试过使用下划线扩展方法将对象的属性从一个复制到另一个原型,以及在两者之间复制各种内容。我认为在这一点上我对作用域和原型有了一些转变,并且希望在使用模块模式时如何最好地做这样的混合。请注意,对象myMixin看起来是什么样子并不重要(无论是将函数添加到原型中,还是模块本身),我只是想找出一些方法来使其工作。
谢谢!
发布于 2014-02-12 03:29:19
,以便某些代码只需调用mixinMethod1()即可在myModule中使用myMixin方法,并具有正确的作用域
那怎么可能。不能通过调用函数来修改作用域,尤其是不能从外部调用。另请参阅Is it possible to import variables in JavaScript?以了解其设计原因。
那么,你能做什么呢?
从模块外部
与module函数的私有作用域无关。显然,您不能使用模块的私有函数。你可以用方法来扩展它的原型(这是最常见的),你甚至可以decorate its constructor function。在这些函数中,您可以使用自己的私有函数,可以是完全静态的函数,也可以是特定于类的函数。
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(){…};
…
}
};
})();有了这样的接口,就可以很容易地构造一个将它用作混合(而不是继承它)的类:
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。尽管如此,我们仍然可以使用依赖注入来显式地提供访问(并具有有效的混合工厂):
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和它们的“特权”对应物之间的理论区别,可以看看this presentation。
发布于 2014-02-12 23:00:01
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将使用我们刚刚定义的definePrivateFunction将私有方法添加到私有作用域:
//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发布于 2014-12-25 08:20:51
理想情况下,我希望混合其他对象中的一些方法作为私有方法,而另一些方法作为公共方法,这样我就可以调用一些“扩展”函数,参数为“
”/“public”。..。
正如已经提到的,没有办法确切地实现这一目标。
所以,
..。使myMixin方法在myModule中可用,只需调用mixinMethod1()并具有正确的作用域,并且:...通过调用module.mixinMethod1()使myMixin方法在myModule中可用,并具有正确的作用域。
关于作用域...这是由函数创建的封闭地址空间。除了closure_s之外,_scope仅在函数运行时在此函数体中可用。它永远不会被操纵/欺骗。
一个正在寻找的术语是上下文。JavaScript在许多方面都是高度动态的,它是建立在后期绑定(调用方法的对象/目标/上下文在运行时进行评估/查找)和two kinds of delegation的基础上的。上下文可以通过“遍历原型链”自动委派,也可以通过每个函数对象都提供的两种调用方法中的一种显式委派- call或apply。
因此,已经处于语言核心层的JavaScript确实提供了一个基于函数的混合模式,它比任何可用的extend(s)或mixin实现都强大,因为它提供了免费的委托,并且能够传递几乎每个被指责的助手所缺乏的状态,除非有人试图以一种相当绕道的方式再次实现这个功能(或者直截了当地说是倒退)。
因为他的explanation,Bergi已经赢得了赏金。在他回答的最后一段中,有一个到我的资源的链接,在给出参考演讲3个月后,这个链接已经过期了。由于没有足够的名誉点,我无法直接评论他的答案。为此,我将借此机会指出我个人对»The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins«的研究和理解的最新情况
再次回答行动组的问题。
我将把最初给出的两个代码示例从假定的模块模式和相当典型地提供的混合代码库改为普通的构造函数,同时我也想称之为“代理的”和/或“双文本”的混合,以便总结一下同时委托两个不同的目标/上下文对象的机制。从而演示了一种基于纯函数的混入模式,该模式可能最接近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.这个特殊的模式也是组成纯基于函数的特征的基础,这些特征依赖于“代理的”Mixins提供的冲突解决功能,这些功能不会公开这种功能。
为了不结束这个理论,将会有一个“现实世界的例子”,由各种完全崇尚DRY方法的可重用的mixins组成一个Queue模块。它还应该回答OP的问题,即如何仅基于基于模块模式和函数的混合组合来实现封装和展示。
var Enumerable_first_last_item = (function (global) {
var
parseFloat = global.parseFloat,
math_floor = global.Math.floor,
// shared code.
first = function () {
return this[0];
},
last = function () {
return this[this.length - 1];
},
item = function (idx) {
return this[math_floor(parseFloat(idx, 10))];
}
;
return function () { // [Enumerable_first_last_item] Mixin.
var enumerable = this;
enumerable.first = first;
enumerable.last = last;
enumerable.item = item;
};
}(window || this));
var Enumerable_first_last_item_proxified = function (list) {
Enumerable_first_last_item.call(list);
// implementing the proxified / bicontextual [Enumerable_first_last_item] Mixin.
var enumerable = this;
enumerable.first = function () {
return list.first();
};
enumerable.last = function () {
return list.last();
};
enumerable.item = function (idx) {
return list.item(idx);
};
};
var Allocable = (function (Array) {
var
array_from = ((typeof Array.from == "function") && Array.from) || (function (array_prototype_slice) {
return function (listType) {
return array_prototype_slice.call(listType);
};
}(Array.prototype.slice))
;
return function (list) { // proxified / bicontextual [Allocable] Mixin.
var
allocable = this
;
allocable.valueOf = allocable.toArray = function () {
return array_from(list);
};
allocable.toString = function () {
return ("" + list);
};
allocable.size = function () {
return list.length;
};
Enumerable_first_last_item_proxified.call(allocable, list);
};
}(Array));
var Queue = (function () { // [Queue] Module.
var
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({type: "empty"});
}*/,
onEmpty = function (queue) {
//queue.dispatchEvent("empty");
},
Queue = function () { // [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); // applying the [Observable] Mixin.
Allocable.call(queue, list); // applying the bicontextual [Allocable] Mixin.
},
isQueue = function (type) {
return !!(type && (type instanceof Queue));
},
createQueue = function () { // [Queue] Factory.
return (new Queue);
}
;
return { // [Queue] Module.
isQueue : isQueue,
create : createQueue
};
}());
var q = Queue.create();
//q.addEventListener("enqueue", function (evt) {/* ... */});
//q.addEventListener("dequeue", function (evt) {/* ... */});
//q.addEventListener("empty", function (evt) {/* ... */});
console.log("q : ", q); // { .., .., .., }
console.log("q.size() : ", q.size()); // 0
console.log("q.valueOf() : ", q.valueOf()); // []
"the quick brown fox jumped over the lazy dog".split(/\s+/).forEach(function (elm/*, idx, arr*/) {
console.log("q.enqueue(\"" + elm + "\")", q.enqueue(elm));
});
console.log("q.size() : ", q.size()); // 9
console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ]
console.log("q.first() : ", q.first()); // "the"
console.log("q.last() : ", q.last()); // "dog"
console.log("q.item(2) : ", q.item(2)); // "brown"
console.log("q.item(5) : ", q.item(5)); // "over"
console.log("q.dequeue()", q.dequeue()); // "the"
console.log("q.dequeue()", q.dequeue()); // "quick"
console.log("q.dequeue()", q.dequeue()); // "brown"
console.log("q.dequeue()", q.dequeue()); // "fox"
console.log("q.dequeue()", q.dequeue()); // "jumped"
console.log("q.size() : ", q.size()); // 4
console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ]
console.log("q.first() : ", q.first()); // "over"
console.log("q.last() : ", q.last()); // "dog"
console.log("q.item(2) : ", q.item(2)); // "lazy"
console.log("q.item(5) : ", q.item(5)); // undefined.as-console-wrapper { max-height: 100%!important; top: 0; }
https://stackoverflow.com/questions/17631517
复制相似问题