javascript – 改进插件架构的实现

我的库中包含一个extend方法,可以将方法添加到核心库中:

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();

最佳答案 你可以做的是将插件方法绑定到一个新对象,这样它们就不会“污染”库.

但是,不要将插件作为库实例的方法,而是将其作为惰性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:带有单例插件且没有getter的版本是:

 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;                                       };
}
点赞