Javascript 改进插件体系结构的实现

Javascript 改进插件体系结构的实现,javascript,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] =

我的库中包含一个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();

这是一个有趣的问题。有一篇关于原型开发的博客文章,很多人都在回避这个事实。我会说这样的话:

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作为作用域调用的。因此,图书馆的所有公共方法都是可用的,您可以使用它们。当然,你要保护插件的隐私。我建议你读一下。我个人经常使用它。这真的帮我省去了很多麻烦

附言。
下面是一个使用上述代码的JSFIDLE

您可以做的是将plugin方法绑定到一个新对象,这样它们就不会“污染”库。
但是,与其将插件作为库实例的方法,不如将其作为惰性getter,这样语法更加流畅,并且只有在需要时才能构建新实例。
插件语法可以简化:只需使用javascript类=>一个在原型上定义了方法的函数。
我还认为,将“extend”作为库的一个属性,而不是在其原型上设置的方法是有意义的,因为任何实例都不应该使用它

有了这个,你可以添加一个插件

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 */ }
 };
请注意,在这个方案中,插件是单例的,即当请求相同的插件时,库的每个实例都将返回相同的对象

如果您希望每个库有一个插件实例,只需让库构造函数保存插件实例即可。(可能在隐藏的财产中)

插件和使用的语法保持不变

编辑:对于较旧的浏览器支持,您仍然可以使用函数而不是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;                                       };
}

您是否允许库的多个实例?您是否希望您的插件像一个单例一样运行,即“shouldbebeprivate”应该是private。。。到插件(无论它与哪个库一起使用)或到插件和库实例?是的,'shouldbeeprivate'应该只能在插件中访问。mmm也许你会在我的回答中看到我的意思,但我的意思是:如果lib1=new Library();lib2=newlibrary(),那么我们应该使用lib1.somePlugin===lib2.somePlugin吗?这看起来确实是一个可能的解决方案。我曾考虑过模块模式,但想到了一种将模块与基础库绑定的好方法。这是一种非常有趣的方法,但我不确定我是否能够摆脱ECMA的困扰,因为我们仍然支持IE7。不过从中也学到了一些。将extend方法作为库的一个属性而不是原型链上的一个方法很好。@MikeRifgin:不客气。查看我的编辑,只需稍作修改,我们就可以让它在较旧的浏览器上工作(不知道这是一个要求)。语法仍然很简单,插件只是一个简单的标准Javascript类,这比任何约定都要好。我认为模块模式为任何编写插件的人创建了一种更干净的方法,所以我将尝试将其应用到我的库中,看看它是如何工作的。如果不是,我可能会回到这个方法注意,另一个解决方案使用“new”操作符,但返回另一个对象,混淆了在js中创建对象的方式。还请注意,您只能将插件添加到Lib实例中,而其他实例将看不到插件,这是毫无用处的。它的“好处”只是复杂性。我建议的解决方案只需要一个构造函数,并允许您选择是否希望插件是单例的。代码行更少,并且没有特殊的约定向用户解释-只需提供一个类即可。但是你可以选择错误的方式!!;-)
 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;                                       };
}