改进插件体系结构的实现
Improving the implementation of a plugin architecture
我的库中包含一个扩展方法,可以将方法添加到核心库中:
library.prototype.extend = function(name,plugin) {
library.prototype[name] = plugin.init;
for (var method in plugin) {
if(method !== 'init') {
library.prototype[name].prototype[method] = plugin[method];
}
}
};
在使用中,它看起来是这样的:
library.prototype.extend('aReallyInterestingPlugin', {
//the init method gets added to the base libraries prototype
init: function() {
this.shouldBePrivate;
},
//another other methods are created as a prototype of the newly added Plugin
anotherMethod : function() {
//this should have access to the shouldBePrivate var
}
});
然后用户可以这样调用插件:
var test = new library();
test.aReallyInterestingPlugin();
这是可行的,但我对这种方法并不满意,我一直在努力寻找一种替代模式来实现这一点。
它的问题是,init和anotherMethod直接添加到库原型链中,因此它们的作用域也是全局库作用域,这很混乱,因为如果声明了任何实例变量(如上面的shouldBePrivate),它们也会添加到库的原型链中。
如何才能添加插件并拥有自己的私有作用域?我想到的一种方法是,插件总是可以作为构造函数调用(因此将有自己的作用域和上下文),但我不确定这有多干净……例如,为了实现这一点,用户在调用插件时必须这样做:
var test = new library();
test.aPlugin = new aReallyInterestingPlugin();
您可以做的是将插件方法绑定到一个新对象,这样它们就不会"污染"库
但是,与其将插件作为Library实例的方法,不如将其作为懒惰的getter,这样语法更灵活,并且只有在需要时才能构建新实例
然后可以简化插件语法:只需使用javascript class=>一个在其原型上定义了方法的函数
我还认为,将"extend"作为Library的属性,而不是在其原型上设置的方法是有意义的,因为任何实例都不应该使用它
有了这个,你可以添加一个带有的插件
library.extend('aReallyInterestingPlugin',AReallyInterestingPluginClass);
使用你可以写
var myLibrary = new Library();
myLibrary.somePlugin.someMethod(arg1, arg2, ...);
代码看起来像:
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
var pluginGetter = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
插件只是标准的javascript类:
function MyPlugin() {
this.pluginProperty1 = 'some value';
}
MyPlugin.prototype = {
method1 : function() { /* do things */} ,
method2 : function() { /* do other things */ }
};
注意,在这个方案中,插件是一个单例,即当被要求使用相同的插件时,Library的每个实例都会返回相同的对象。
如果您喜欢每个库一个插件实例,只需让库构造函数保存插件实例即可。(可能在隐藏的财产中)。
function Library() {
// the code allready here...
var pluginInstances = {};
Object.defineProperty(this, 'pluginInstances',
{ get : function() { return pluginInstances }, enumerable : false });
}
library.extend = function(name,plugin) {
var pluginGetter = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
插件和用法的语法保持不变。
编辑:对于旧的浏览器支持,您仍然可以使用函数而不是getter:
function Library() {
// the code allready here...
this.pluginInstances= {} ;
}
library.extend = function(name,plugin) {
Library.prototype[name] = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
} ;
使用它你会做:
var myLibrary = new Library();
myLibrary.somePlugin().someMethod(arg1, arg2, ...);
编辑2:带有单例插件和没有getters的版本是:
function Library() { /* same code */ }
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
Library.prototype[name] = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
}
这是一个有趣的问题。有一篇关于原型化开发的博客文章,事实上很多人都在回避它
var Library = function() {
var api;
var private = "some value";
var privateMethod = function() {
console.log(private);
}
var registerPlugin = function(name, plugin) {
api[name] = plugin.call(api);
}
var publicMethod = function() {
privateMethod();
}
return api = {
show: publicMethod,
plugin: registerPlugin
}
}
// usage of the library
var library = new Library();
library.show();
// registering a plugin
library.plugin("awesome", function() {
var api, library = this;
var pluginVar = "That's a plugin";
var pluginMethod = function() {
console.log(pluginVar);
library.show();
}
return api = {
gogo: pluginMethod
}
});
// calling a method of the plugin
library.awesome.gogo();
库只是一个函数,它有自己的作用域、自己的私有和公共方法,并导出API。插件实际上是另一个具有相同功能的函数,但它是以库的API作为作用域调用的。因此,库的所有公共方法都是可用的,您可以使用它们。当然,你要保护插件的隐私。我建议阅读有关揭示模块模式的内容。我个人经常使用它。这真的为我省去了很多麻烦。
p.S。下面是一个使用上面代码的jsfiddlehttp://jsfiddle.net/XyTJF/
- 如何在js中实现树结构类型的表单动态填充
- 与您的操作系统或体系结构不兼容:fsevents@1.0.11.
- 在Flux体系结构中,如何管理存储相同类型数据的存储
- 具有http服务器、websocket和express的体系结构
- 改进插件体系结构的实现
- Chrome 扩展消息传递体系结构
- Web应用程序的体系结构/设计,对另一台服务器进行了大量Ajax调用
- RESTful Web 应用程序中的客户端服务器体系结构
- 基于 AJAX 的聊天的数据库体系结构
- Socket.io 向某些套接字(体系结构)发出消息
- 用于第三方 API 调用的 Web 应用体系结构
- MEAN 堆栈上登录系统的体系结构
- 网络表单和脚本服务体系结构
- 网站中的单页体系结构或应用程序在 html 导航中使用 servlet 而不打开新页面
- 需要经过身份验证的 WCF REST 体系结构建议
- 需要哪种体系结构才能允许多个 Web 应用协同工作,这些应用可以单独更新
- Angularjs中ajax的单一方法体系结构
- ReactJs组件体系结构
- 在Flux体系结构中,全局调度程序是否有害
- JavaScript体系结构-中介,何时使用它们