用于处理私有函数的jQuery插件设计模式(通用实践?)

我一直在开发jQuery插件很长一段时间了,我想我现在知道如何设计一个好的插件。 然而,有一个问题一直困扰着我,那就是如何以强大而优雅的方式处理私有function。

我的插件通常看起来像这样:

(function($) { $.fn.myplugin = function(...) { ... // some shared functionality, for example: this.css('background-color', 'green'); ... }; $.fn.mypluginAnotherPublicMethod = function(...) { ... // some shared functionality, for example: this.css('background-color', 'red'); ... }; }(jQuery)); 

现在我的问题是:如何巧妙地干掉共享function? 一个明显的解决方案是将它放在插件命名空间中的函数中:

 var fill = function($obj, color) { $obj.css('background-color', color); }; 

虽然这个解决方案很有效且命名很好,但我真的不喜欢它。 原因很简单:我必须传递jQuery对象。 即我必须这样称呼它: fill(this, 'red'); 虽然我想这样称呼: this.fill('red');

当然,我们可以通过简单地将fill放入jQuery.fn来实现这个结果。 但这感觉非常不舒服。 想象一下,基于这种方法开发了十个插件,每个插件将五个“私有”函数放入jQuery函数命名空间。 它最终陷入了混乱。 我们可以通过在每个函数前面加上它们所属插件的名称来缓解这​​种情况,但这并没有真正使它更具吸引力。 这些函数应该是插件的私有函数,因此我们根本不想将它们暴露给外部世界(至少不是直接暴露)。

所以我的问题是:你们中是否有人有关于如何充分利用两个世界的建议。 那是; 插件代码能够以类似于this.fill('red') (或this.myplugin.fill('red')或甚至this.myplugin().fill('red')的方式调用’private’插件函数this.myplugin().fill('red')等),同时防止jQuery函数命名空间污染。 当然它应该是轻量级的,因为这些私有函数可能会被频繁调用。


更新 :感谢您的建议。

我特别喜欢David的定义一个包含’private’函数并包装jQuery对象的对象类型的想法。 唯一的问题是它仍然不允许我链接“私人”和“公共”function。 这就是想要使用像this.fill('red')这样的语法的重要原因。

我最终找到了一个我认为不是非常优雅的解决方案,但却吸引了“两全其美”的原因:

 $.fn.chain = function(func) { return func.apply(this, Array.prototype.slice.call(arguments, 1)); }; 

这允许构造如:

 this. find('.acertainclass'). chain(fill, 'red'). click(function() { alert("I'm red"); }); 

我在其他地方交叉发布了我的问题,其中也收集了一些有趣的回答:

  • http://forum.jquery.com/topic/jquery-plugin-design-pattern-common-practice-for-dealing-with-private-functions
  • http://groups.google.com/group/jquery-en/browse_thread/thread/fa8ccef21ccc589a

首先要做的事情是 :如果你想打电话给这样的话。 this.fill('red'); 是jQuery的一个实例,你必须扩展jQuery原型并使fill() “公开”。 jQuery提供了使用所谓的“插件”扩展它的原型的指南,可以使用$.fn.fill添加$.fn.fill ,这与jQuery.prototype.fill相同。

在jQuery回调中, 通常是对HTML元素的引用,并且您无法向那些(还)添加原型。 这就是为什么jQuery包装元素并返回可以轻松扩展的jQuery实例的原因之一。

使用(function(){})(); 语法,你可以动态创建和执行“私人”javascript,当它完成后它们都会消失。 使用这种技术,您可以创建自己的类jQuery语法,将jQuery包装到您自己的私有可链接对象中。

 (function(){ var P = function(elem) { return new Private(elem); }; var Private = function(elem) { this.elem = jQuery(elem); } Private.prototype = { elem: null, fill: function(col) { this.elem.css('background',col); return this; }, color: function(col) { this.elem.css('color', col); return this; } } $.fn.myplugin = function() { P(this).fill('red'); }; $.fn.myotherplugin = function() { P(this).fill('yellow').color('green'); }; })(); $('.foo').myplugin(); $('.bar').myotherplugin(); console.log(typeof P === 'undefined') // should print 'true' 

这样,P代表您自己的“私有”function工具箱。 它们将无法在代码中的任何其他位置或jQuery命名空间中使用,除非您将它们附加到某处。 你可以在Private对象中添加任意数量的方法,只要你返回 ,你也可以像我在示例中那样链接它们jQuery样式。

怎么样(在插件的范围内):

 var fill = function () { (function (color) { this.css ('backgrorund-color', color); //.. your stuff here ... }).apply (this, arguments); } $.fn.myplugin = function () { fill ('green'); } 

这样,fill将保留您所在的jQuery上下文,并且仍然是您的插件的私有


修改:以上是不正确的范围,请尝试以下代码:

 var fill = function (color) { if (!$this) return; // break if not within correct context $this.css ('backgrorund-color', color); //.. your stuff here ... } $.fn.myplugin = function () { var $this = $(this); // local ref to current context fill ('green'); } 

您可能想看看如何实现jQuery UI Widget Factory 。

基本方法是这样的:

 (function($){ $.fn.myplugin = function(method) { if (mp[method]) // map $('foo').myplugin('bar', 'baz') to mp.bar('baz') { return mp[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === 'object' || ! method) { return mp.init.apply(this, arguments); // if called without arguments, init } else { $.error('Method ' + method + ' does not exist on $.myplugin'); } }; // private methods, internally accessible via this.foo, this.bar var foo = function() { … }; var bar = function() { … }; var private = { // alternative approach to private methods, accessible via this.private.foo foo : function() { … }, bar : function() { … } } var mp = { // public methods, externally accessible via $.myplugin('foo', 'bar') init : function( options ) { return this.each(function() { // do init stuff } }, foo : function() { … }, bar : function() { … } }; })(jQuery); 

不幸的是,在javascript中永远不能使用“this”前缀调用“私有”方法(或任何属性)。 任何被称为this.myFunc(myArgs)必须公开。

并且“私有”方法只能在定义它们的范围内调用。

您的原始解决方案是唯一可行的解​​决方案。 是的,这是一个痛苦的过程,但是如果你的不可能的请求成为可能,那就没有更多的冗长:

 this.fill('green'); //or fill(this,'green'); 

如您所见,它们在代码中占用的字符数完全相同。

很抱歉,但是你坚持使用它作为解决方案,除非你想创建一个新的命名空间并使它们不是私有的 – 这只会增加你需要编写的代码量,即你间接调用的代码“没有直接暴露”:

 this.myplugin.fill('green'); 

……更加冗长,因此有点挫败了目的。

Javascript与其他语言不同,本身没有“私人”成员,只有在闭包内可访问的成员,有时可以以类似于私有成员的方式使用,但更多的是“解决方法”,而不是“实际交易“你正在寻找的私人会员。

可能很难与此达成协议(我经常挣扎),但不要试图将javascript转化为你从其他语言中理解的东西,把它当作它的内容……