javascript jQuery 插件模板 - 最佳实践、约定、性能和内存影响

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5980194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 19:01:37  来源:igfitidea点击:

jQuery plugin template - best practice, convention, performance and memory impact

javascriptjqueryjquery-plugins

提问by Ed .

I've started to write few jQuery plugins and figured it'd be nice to setup my IDE with a jQuery plugin template.

我已经开始编写一些 jQuery 插件,并认为使用 jQuery 插件模板设置我的 IDE 会很好。

I have been reading some articles and posts on this site related to plugin convention, design, etc.. and thought I'd try and consolidate all of that.

我一直在阅读本网站上与插件约定、设计等相关的一些文章和帖子。我想我会尝试整合所有这些。

Below is my template, I am looking to use it frequently so was keen to ensure it generally conforms to jQuery plugin design convention and whether the idea of having multiple internal methods (or even its general design) would impact performance and be prone to memory issues.

下面是我的模板,我希望经常使用它,所以很想确保它通常符合 jQuery 插件设计约定,以及拥有多个内部方法(甚至其通用设计)的想法是否会影响性能并容易出现内存问题.

(function($)
{
    var PLUGIN_NAME = "myPlugin"; // TODO: Plugin name goes here.
    var DEFAULT_OPTIONS =
    {
        // TODO: Default options for plugin.
    };
    var pluginInstanceIdCount = 0;

    var I = function(/*HTMLElement*/ element)
    {
        return new Internal(element);
    };

    var Internal = function(/*HTMLElement*/ element)
    {
        this.$elem = $(element);
        this.elem = element;
        this.data = this.getData();

        // Shorthand accessors to data entries:
        this.id = this.data.id;
        this.options = this.data.options;
    };

    /**
     * Initialises the plugin.
     */
    Internal.prototype.init = function(/*Object*/ customOptions)
    {
        var data = this.getData();

        if (!data.initialised)
        {
            data.initialised = true;
            data.options = $.extend(DEFAULT_OPTIONS, customOptions);

            // TODO: Set default data plugin variables.
            // TODO: Call custom internal methods to intialise your plugin.
        }
    };

    /**
     * Returns the data for relevant for this plugin
     * while also setting the ID for this plugin instance
     * if this is a new instance.
     */
    Internal.prototype.getData = function()
    {
        if (!this.$elem.data(PLUGIN_NAME))
        {
            this.$elem.data(PLUGIN_NAME, {
                id : pluginInstanceIdCount++,
                initialised : false
            });
        }

        return this.$elem.data(PLUGIN_NAME);
    };

    // TODO: Add additional internal methods here, e.g. Internal.prototype.<myPrivMethod> = function(){...}

    /**
     * Returns the event namespace for this widget.
     * The returned namespace is unique for this widget
     * since it could bind listeners to other elements
     * on the page or the window.
     */
    Internal.prototype.getEventNs = function(/*boolean*/ includeDot)
    {
        return (includeDot !== false ? "." : "") + PLUGIN_NAME + "_" + this.id;
    };

    /**
     * Removes all event listeners, data and
     * HTML elements automatically created.
     */
    Internal.prototype.destroy = function()
    {
        this.$elem.unbind(this.getEventNs());
        this.$elem.removeData(PLUGIN_NAME);

        // TODO: Unbind listeners attached to other elements of the page and window.
    };

    var publicMethods =
    {
        init : function(/*Object*/ customOptions)
        {
            return this.each(function()
            {
                I(this).init(customOptions);
            });
        },

        destroy : function()
        {
            return this.each(function()
            {
                I(this).destroy();
            });
        }

        // TODO: Add additional public methods here.
    };

    $.fn[PLUGIN_NAME] = function(/*String|Object*/ methodOrOptions)
    {
        if (!methodOrOptions || typeof methodOrOptions == "object")
        {
            return publicMethods.init.call(this, methodOrOptions);
        }
        else if (publicMethods[methodOrOptions])
        {
            var args = Array.prototype.slice.call(arguments, 1);

            return publicMethods[methodOrOptions].apply(this, args);
        }
        else
        {
            $.error("Method '" + methodOrOptions + "' doesn't exist for " + PLUGIN_NAME + " plugin");
        }
    };
})(jQuery);

Thanks in advance.

提前致谢。

采纳答案by Raynos

[Edit]7 months later

[编辑]7 个月后

Quoting from the github project

引自github项目

jQuery is no good, and jQuery plugins is not how do modular code.

jQuery 不行,而且jQuery 插件不是怎么模块化代码的。

Seriously "jQuery plugins" are not a sound architecture strategy. Writing code with a hard dependency on jQuery is also silly.

严肃地说,“jQuery 插件”并不是一个合理的架构策略。编写对 jQuery 有严格依赖的代码也是很愚蠢的。

[Original]

[原来的]

Since I gave critique about this template I will propose an alternative.

由于我对这个模板提出了批评,我将提出一个替代方案。

To make live easier this relies on jQuery1.6+ and ES5 (use the ES5 Shim).

为了让生活更轻松,这依赖于jQuery1.6+ 和 ES5(使用ES5 Shim)。

I've spend some time re-designing the plugin template you've given and rolled out my own.

我花了一些时间重新设计您提供的插件模板并推出了我自己的插件模板。

Links:

链接:

Comparison:

比较:

I've refactored the template so that it's split into boilerplate (85%) and scaffolding code (15%). The intention is that you only have to edit the scaffolding code and you can keep leave boilerplate code untouched. To achieve this I've used

我重构了模板,使其分为样板 (85%) 和脚手架代码 (15%)。目的是您只需要编辑脚手架代码,并且可以保持样板代码不变。为了实现这一点,我用过

  • inheritancevar self = Object.create(Base)Rather then editing the Internalclass you have directly you should be editing a sub class. All your template / default functionality should be in a base class (called Basein my code).
  • conventionself[PLUGIN_NAME] = main;By convention the plugin defined on jQuery will call the method define on self[PLUGIN_NAME]by default. This is considered the mainplugin method and has a seperate external method for clarity.
  • monkey patching$.fn.bind = function _bind ...Use of monkey patching means that the event namespacing is done automatically for you under the hood. This functionality is free and does not come at the cost of readability (calling getEventNSall the time).
  • 继承var self = Object.create(Base)而不是Internal直接编辑您拥有的类,您应该编辑子类。你所有的模板/默认功能都应该在一个基类中(Base在我的代码中调用)。
  • 约定self[PLUGIN_NAME] = main;按照约定,jQuery 上定义的插件self[PLUGIN_NAME]默认调用定义的方法。这被认为是main插件方法,为了清楚起见,它有一个单独的外部方法。
  • 猴子补丁$.fn.bind = function _bind ...使用猴子补丁意味着事件命名空间在幕后自动为您完成。此功能是免费的,并且不以可读性为代价(一直调用getEventNS)。

OO Techniques

面向对象技术

It's better to stick to proper JavaScript OO rather then classical OO emulation. To achieve this you should use Object.create. (which ES5 just use the shim to upgrade old browsers).

最好坚持正确的 JavaScript OO 而不是经典的 OO 模拟。要实现这一点,您应该使用Object.create. (ES5 只是使用 shim 来升级旧浏览器)。

var Base = (function _Base() {
    var self = Object.create({}); 
    /* ... */
    return self;
})();

var Wrap = (function _Wrap() {
    var self = Object.create(Base);
    /* ...  */
    return self;
})();

var w = Object.create(Wrap);

This is different from the standard newand .prototypebased OO people are used to. This approach is preferred because it re-inforces the concept that there are only Objects in JavaScript and it's a prototypical OO approach.

new.prototype人们习惯的标准和基础 OO不同。这种方法是首选,因为它强化了 JavaScript 中只有对象的概念,并且它是一种典型的 OO 方法。

[getEventNs]

[ getEventNs]

As mentioned this method has been refactored away by overriding .bindand .unbindto automatically inject namespaces. These methods are overwritten on the private version of jQuery $.sub(). The overwritten methods behave the same way as your namespacing does. It namespaces events uniquely based on plugin and instance of a plugin wrapper around a HTMLElement (Using .ns.

如前所述,此方法已通过覆盖.bind.unbind自动注入命名空间进行重构。这些方法在 jQuery 的私有版本上被覆盖$.sub()。被覆盖的方法的行为方式与您的命名空间相同。它根据插件和围绕 HTMLElement 的插件包装器实例唯一地命名事件(使用.ns.

[getData]

[ getData]

This method has been replaced with a .datamethod that has the same API as jQuery.fn.data. The fact that it's the same API makes it easier to use, its basically a thin wrapper around jQuery.fn.datawith namespacing. This allows you to set key/value pair data that is immediatley stored for that plugin only. Multiple plugins can use this method in parallel without any conflicts.

此方法已被替换为.data具有与 相同 API 的方法jQuery.fn.data。它是相同的 API 的事实使它更易于使用,它基本上是一个jQuery.fn.data带有命名空间的薄包装器。这允许您设置仅为该插件存储的键/值对数据。多个插件可以并行使用此方法而不会发生任何冲突。

[publicMethods]

[ publicMethods]

The publicMethods object has been replaced by any method being defined on Wrapbeing automatically public. You can call any method on a Wrapped object directly but you do not actually have access to the wrapped object.

publicMethods 对象已被任何定义Wrap为自动公开的方法所取代。您可以直接调用 Wrapped 对象上的任何方法,但您实际上无权访问包装对象。

[$.fn[PLUGIN_NAME]]

[ $.fn[PLUGIN_NAME]]

This has been refactored so it exposes a more standardized API. This api is

这已被重构,因此它公开了更标准化的 API。这个api是

$(selector).PLUGIN_NAME("methodName", {/* object hash */}); // OR
$(selector).PLUGIN_NAME({/* object hash */}); // methodName defaults to PLUGIN_NAME

the elements in the selector are automatically wrapped in the Wrapobject, the method is called or each selected element from the selector and the return value is always a $.Deferredelement.

选择器中的元素自动包装在Wrap对象中,调用该方法或从选择器中选择每个元素,返回值始终是一个$.Deferred元素。

This standardizes the API and the return type. You can then call .thenon the returned deferred to get out the actual data you care about. The use of deferred here is very powerful for abstraction away whether the plugin is synchronous or asynchronous.

这标准化了 API 和返回类型。然后你可以调用.then返回的 deferred 来获取你关心的实际数据。在这里使用延迟对于抽象插件是同步的还是异步的非常强大。

_create

_create

A caching create function has been added. This is called to turn a HTMLElementinto a Wrapped element and each HTMLElement will only be wrapped once. This caching gives you a solid reduction in memory.

添加了缓存创建功能。这被调用以将 aHTMLElement转换为 Wrapped 元素,并且每个 HTMLElement 只会被包装一次。这种缓存可以显着减少内存。

$.PLUGIN_NAME

$.PLUGIN_NAME

Added another public method for the plugin (A total of two!).

为插件添加了另一个公共方法(一共两个!)。

$.PLUGIN_NAME(elem, "methodName", {/* options */});
$.PLUGIN_NAME([elem, elem2, ...], "methodName", {/* options */});
$.PLUGIN_NAME("methodName", { 
  elem: elem, /* [elem, elem2, ...] */
  cb: function() { /* success callback */ }
  /* further options */
});

All parameters are optional. elemdefaults to <body>, "methodName"defaults to "PLUGIN_NAME"and {/* options */}defaults to {}.

所有参数都是可选的。elem默认为<body>"methodName"默认为"PLUGIN_NAME"{/* options */}默认为{}

This API is very flexible (with 14 method overloads!) and standard enough to get used to the syntnax for every method your plugin will expose.

这个 API 非常灵活(有 14 个方法重载!)并且足够标准以适应插件将公开的每个方法的语法。

Public exposure

公众暴露

The Wrap, createand $objects are exposed globally. This will allow advanced plugin users maximum flexibility with your plugin. They can use createand the modified subbed $in their development and they can also monkey patch Wrap. This allows for i.e. hooking into your plugin methods. All three of these are marked with a _in front of their name so they are internal and using them breaks the garantuee that your plugin works.

Wrapcreate$对象是全球范围内曝光。这将允许高级插件用户最大程度地灵活使用您的插件。他们可以在他们的开发中使用create和修改后的替换$,他们也可以使用猴子补丁Wrap。这允许 ie 挂钩到您的插件方法。所有这三个都_在其名称前面标有,因此它们是内部的,使用它们会破坏您的插件工作的保证。

The internal defaultsobject is also exposed as $.PLUGIN_NAME.global. This allows users to override your defaults and set plugin global defaults. In this plugin setup all hashes past into methods as objects are merged with the defaults, so this allows users to set global defaults for all your methods.

内部defaults对象也公开为$.PLUGIN_NAME.global. 这允许用户覆盖您的默认值并设置插件 global defaults。在这个插件设置中,所有散列都传递到方法中,因为对象与默认值合并,因此这允许用户为所有方法设置全局默认值。

Actual Code

实际代码

(function($, jQuery, window, document, undefined) {
    var PLUGIN_NAME = "Identity";
    // default options hash.
    var defaults = {
        // TODO: Add defaults
    };

    // -------------------------------
    // -------- BOILERPLATE ----------
    // -------------------------------

    var toString = Object.prototype.toString,
        // uid for elements
        uuid = 0,
        Wrap, Base, create, main;

    (function _boilerplate() {
        // over-ride bind so it uses a namespace by default
        // namespace is PLUGIN_NAME_<uid>
        $.fn.bind = function  _bind(type, data, fn, nsKey) {
            if (typeof type === "object") {
                for (var key in type) {
                    nsKey = key + this.data(PLUGIN_NAME)._ns;
                    this.bind(nsKey, data, type[key], fn);
                }
                return this;
            }

            nsKey = type + this.data(PLUGIN_NAME)._ns;
            return jQuery.fn.bind.call(this, nsKey, data, fn);
        };

        // override unbind so it uses a namespace by default.
        // add new override. .unbind() with 0 arguments unbinds all methods
        // for that element for this plugin. i.e. calls .unbind(_ns)
        $.fn.unbind = function _unbind(type, fn, nsKey) {
            // Handle object literals
            if ( typeof type === "object" && !type.preventDefault ) {
                for ( var key in type ) {
                    nsKey = key + this.data(PLUGIN_NAME)._ns;
                    this.unbind(nsKey, type[key]);
                }
            } else if (arguments.length === 0) {
                return jQuery.fn.unbind.call(this, this.data(PLUGIN_NAME)._ns);
            } else {
                nsKey = type + this.data(PLUGIN_NAME)._ns;
                return jQuery.fn.unbind.call(this, nsKey, fn);    
            }
            return this;
        };

        // Creates a new Wrapped element. This is cached. One wrapped element 
        // per HTMLElement. Uses data-PLUGIN_NAME-cache as key and 
        // creates one if not exists.
        create = (function _cache_create() {
            function _factory(elem) {
                return Object.create(Wrap, {
                    "elem": {value: elem},
                    "$elem": {value: $(elem)},
                    "uid": {value: ++uuid}
                });
            }
            var uid = 0;
            var cache = {};

            return function _cache(elem) {
                var key = "";
                for (var k in cache) {
                    if (cache[k].elem == elem) {
                        key = k;
                        break;
                    }
                }
                if (key === "") {
                    cache[PLUGIN_NAME + "_" + ++uid] = _factory(elem);
                    key = PLUGIN_NAME + "_" + uid;
                } 
                return cache[key]._init();
            };
        }());

        // Base object which every Wrap inherits from
        Base = (function _Base() {
            var self = Object.create({});
            // destroy method. unbinds, removes data
            self.destroy = function _destroy() {
                if (this._alive) {
                    this.$elem.unbind();
                    this.$elem.removeData(PLUGIN_NAME);
                    this._alive = false;    
                }
            };

            // initializes the namespace and stores it on the elem.
            self._init = function _init() {
                if (!this._alive) {
                    this._ns = "." + PLUGIN_NAME + "_" + this.uid;
                    this.data("_ns", this._ns);    
                    this._alive = true;
                }
                return this;
            };

            // returns data thats stored on the elem under the plugin.
            self.data = function _data(name, value) {
                var $elem = this.$elem, data;
                if (name === undefined) {
                    return $elem.data(PLUGIN_NAME);
                } else if (typeof name === "object") {
                    data = $elem.data(PLUGIN_NAME) || {};
                    for (var k in name) {
                        data[k] = name[k];
                    }
                    $elem.data(PLUGIN_NAME, data);
                } else if (arguments.length === 1) {
                    return ($elem.data(PLUGIN_NAME) || {})[name];
                } else  {
                    data = $elem.data(PLUGIN_NAME) || {};
                    data[name] = value;
                    $elem.data(PLUGIN_NAME, data);
                }
            };
                return self;
        })();

        // Call methods directly. $.PLUGIN_NAME(elem, "method", option_hash)
        var methods = jQuery[PLUGIN_NAME] = function _methods(elem, op, hash) {
            if (typeof elem === "string") {
                hash = op || {};
                op = elem;
                elem = hash.elem;
            } else if ((elem && elem.nodeType) || Array.isArray(elem)) {
                if (typeof op !== "string") {
                    hash = op;
                    op = null;
                }
            } else {
                hash = elem || {};
                elem = hash.elem;
            }

            hash = hash || {}
            op = op || PLUGIN_NAME;
            elem = elem || document.body;
            if (Array.isArray(elem)) {
                var defs = elem.map(function(val) {
                    return create(val)[op](hash);    
                });
            } else {
                var defs = [create(elem)[op](hash)];    
            }

            return $.when.apply($, defs).then(hash.cb);
        };

        // expose publicly.
        Object.defineProperties(methods, {
            "_Wrap": {
                "get": function() { return Wrap; },
                "set": function(v) { Wrap = v; }
            },
            "_create":{
                value: create
            },
            "_$": {
                value: $    
            },
            "global": {
                "get": function() { return defaults; },
                "set": function(v) { defaults = v; }
             }
        });

        // main plugin. $(selector).PLUGIN_NAME("method", option_hash)
        jQuery.fn[PLUGIN_NAME] = function _main(op, hash) {
            if (typeof op === "object" || !op) {
                hash = op;
                op = null;
            }
            op = op || PLUGIN_NAME;
            hash = hash || {};

            // map the elements to deferreds.
            var defs = this.map(function _map() {
                return create(this)[op](hash);
            }).toArray();

            // call the cb when were done and return the deffered.
            return $.when.apply($, defs).then(hash.cb);

        };
    }());

    // -------------------------------
    // --------- YOUR CODE -----------
    // -------------------------------

    main = function _main(options) {
        this.options = options = $.extend(true, defaults, options); 
        var def = $.Deferred();

        // Identity returns this & the $elem.
        // TODO: Replace with custom logic
        def.resolve([this, this.elem]);

        return def;
    }

    Wrap = (function() {
        var self = Object.create(Base);

        var $destroy = self.destroy;
        self.destroy = function _destroy() {
            delete this.options;
            // custom destruction logic
            // remove elements and other events / data not stored on .$elem

            $destroy.apply(this, arguments);
        };

        // set the main PLUGIN_NAME method to be main.
        self[PLUGIN_NAME] = main;

        // TODO: Add custom logic for public methods

        return self;
    }());

})(jQuery.sub(), jQuery, this, document);

As can be seen the code your supposed to edit is below the YOUR CODEline. The Wrapobject acts similarly to your Internalobject.

可以看出,您应该编辑的代码位于该YOUR CODE行下方。该Wrap对象的行为类似于您的Internal对象。

The function mainis the main function called with $.PLUGIN_NAME()or $(selector).PLUGIN_NAME()and should contain your main logic.

该函数main是用$.PLUGIN_NAME()or调用的主要函数$(selector).PLUGIN_NAME(),应该包含您的主要逻辑。

回答by Kees C. Bakker

A while back I've build a plugin generator based on a blog article I have read: http://jsfiddle.net/KeesCBakker/QkPBF/. It might be of use. It is fairly basic and straight forward. Any comments would be very welcome.

不久前,我根据我读过的一篇博客文章构建了一个插件生成器:http: //jsfiddle.net/KeesCBakker/QkPBF/。它可能有用。这是相当基本和直接的。任何评论将非常受欢迎。

You can fork your own generator and change it to your needs.

您可以分叉自己的生成器并根据需要进行更改。

Ps. This is the generated body:

附言。这是生成的身体:

(function($){

    //My description
    function MyPluginClassName(el, options) {

        //Defaults:
        this.defaults = {
            defaultStringSetting: 'Hello World',
            defaultIntSetting: 1
        };

        //Extending options:
        this.opts = $.extend({}, this.defaults, options);

        //Privates:
        this.$el = $(el);
    }

    // Separate functionality from object creation
    MyPluginClassName.prototype = {

        init: function() {
            var _this = this;
        },

        //My method description
        myMethod: function() {
            var _this = this;
        }
    };

    // The actual plugin
    $.fn.myPluginClassName = function(options) {
        if(this.length) {
            this.each(function() {
                var rev = new MyPluginClassName(this, options);
                rev.init();
                $(this).data('myPluginClassName', rev);
            });
        }
    };
})(jQuery);

回答by centurian

I've been googling and landed here so, I have to post some ideas: first I agree with @Raynos.

我一直在谷歌搜索并登陆这里,所以我必须发布一些想法:首先我同意@Raynos。

The most code out there that tries to build a jQuery plugin actually...is not a plugin! It's just an object stored in memory which is refered by the data property of a node/element. That's because jQuery should be seen and used as a tool side by side with a class library (to remedy js inconsistencies from OO architecture) to build better code and yes this is not bad at all!

大多数尝试构建 jQuery 插件的代码实际上......不是插件!它只是一个存储在内存中的对象,由节点/元素的 data 属性引用。那是因为 jQuery 应该被视为和类库并排使用的工具(以纠正 OO 架构中的 js 不一致)以构建更好的代码,是的,这一点也不差!

If you don't like classical OO behaviour stick to a prototypal library like clone.

如果您不喜欢经典的 OO 行为,请坚持使用像clone这样的原型库。

So what our options really?

那么我们的选择究竟是什么?

  • use JQueryUI/Widget or a similar library that hides technicalities and provides abstraction
  • don't use them because of complexities, learning curve and god knows future changes
  • don't use them becuase you want to insist on modular design, build small-increase later
  • don't use them because you might want porting/connecting your code with different libraries.
  • 使用 JQueryUI/Widget 或类似的库来隐藏技术细节并提供抽象
  • 不要因为复杂性、学习曲线和天知道未来的变化而使用它们
  • 不要使用它们,因为你要坚持模块化设计,以后再小幅增加
  • 不要使用它们,因为您可能希望将代码移植/连接到不同的库。

Suppose the issues addressed by the following scenario (see the complexities from this question: Which jQuery plugin design pattern should I use?):

假设以下场景解决了问题(请参阅这个问题的复杂性:我应该使用哪种 jQuery 插件设计模式?):

we have nodes A, B and C that store an object reference into their dataproperty

some of them store info in public andprivate accessible internal objects, some classes of these objects are connected with inheritance, all of these nodes also need some private andpublic singletonsto work best.

我们有节点 A、B 和 C,它们将对象引用存储到它们的data属性中

其中一些将信息存储在公共私有可访问的内部对象中,这些对象的某些类与继承相关,所有这些节点还需要一些私有公共单例才能发挥最佳作用。

What would we do? See the drawning:

我们会怎么做?看图:

classes : |  A        B         C
------------------case 1----------
members   |  |        |         |
  of      |  v        v         v
an object | var a=new A, b=new B,  c=new C
  at      |     B extends A
node X :  |  a, b, c : private
------------------case 2---------
members   |  |        |         |
  of      |  v        v         v
an object | var aa=new A, bb=new B, cc=new C
  at      |     BB extends AA
node Y :  |  aa, bb, cc : public
-------------------case 3--------
members   |  |        |         |
  of      |  v        v         v
an object | var d= D.getInstance() (private),
  at      |     e= E.getInstance() (public)
node Z :  |     D, E : Singletons

as you can see every node refers to an object - a jQuery approach - but these objects change wildely; they contain object-properties with different data stored in or, even singletons that should be...single in memory like the prototype functions of the objects. We don't want every object's function belonging to class Ato be repeatedly duplicated in memoryin every node's object!

正如你所看到的,每个节点都指向一个对象——一种 jQuery 方法——但这些对象变化很大;它们包含存储在不同数据中的对象属性,或者甚至应该是......在内存中像对象的原型函数一样的单例。我们不希望属于每个对象的功能在每个节点的对象的内存class A重复复制

Before my answersee a common approach I've seen in jQuery plugins - some of them very popular but I don't say names:

在我回答之前,先看看我在 jQuery 插件中看到的一种常见方法——其中一些非常流行,但我没有说名字:

(function($, window, document, undefined){
   var x = '...', y = '...', z = '...',
       container, $container, options;
   var myPlugin = (function(){ //<----the game is lost!
      var defaults = {

      };
      function init(elem, options) {
         container = elem;
         $container = $(elem);
         options = $.extend({}, defaults, options);
      }
      return {
         pluginName: 'superPlugin',
         init: function(elem, options) {
            init(elem, options);
         }
      };
   })();
   //extend jquery
   $.fn.superPlugin = function(options) {
      return this.each(function() {
         var obj = Object.create(myPlugin); //<---lose, lose, lose!
         obj.init(this, options);
         $(this).data(obj.pluginName, obj);
      });
   };

}(jQuery, window, document));

I was watching some slides at: http://www.slideshare.net/benalman/jquery-plugin-creationfrom Ben Alman where he refers at slide 13 to object literalsas singletonsand that just knock me over: this is what the above plugin does, it creates one singleton with no chance whatsoverto alter it's internal state!!!

我正在观看一些幻灯片:http://www.slideshare.net/benalman/jquery-plugin-creation来自 Ben Alman,他在幻灯片 13中将对象文字称为单例,这让我感到震惊:这就是上面的内容插件确实如此,它创建了一个单身人士,没有机会改变它的内部状态!!!

Furthermore, at the jQuery part it stores a common referenceto every single node!

此外,在 jQuery 部分,它存储对每个节点的公共引用

My solution uses a factoryto keep internal state and return an object plus it can be expanded with a classlibrary and split in different files:

我的解决方案使用的工厂,以保持内部状态,并返回一个对象,再加上它可以用一个可扩展的类库,并分成不同的文件:

;(function($, window, document, undefined){
   var myPluginFactory = function(elem, options){
   ........
   var modelState = {
      options: null //collects data from user + default
   };
   ........
   function modeler(elem){
      modelState.options.a = new $$.A(elem.href);
      modelState.options.b = $$.B.getInstance();
   };
   ........
   return {
         pluginName: 'myPlugin',
         init: function(elem, options) {
            init(elem, options);
         },
         get_a: function(){return modelState.options.a.href;},
         get_b: function(){return modelState.options.b.toString();}
      };
   };
   //extend jquery
   $.fn.myPlugin = function(options) {
      return this.each(function() {
         var plugin = myPluginFactory(this, options);
         $(this).data(plugin.pluginName, plugin);
      });
   };
}(jQuery, window, document));

My project: https://github.com/centurianii/jsplugin

我的项目:https: //github.com/centurianii/jsplugin

See: http://jsfiddle.net/centurianii/s4J2H/1/

见:http: //jsfiddle.net/centurianii/s4J2H/1/

回答by DevWL

How about something like this ? It's much clearer but again it would be nice to hear from you if you can improve it without overcomplicating its simplicity.

这样的事情怎么样?它更清晰,但如果您可以改进它而不会使它的简单性过于复杂,那么很高兴收到您的来信。

// jQuery plugin Template
(function($){
    $.myPlugin = function(options) { //or use "$.fn.myPlugin" or "$.myPlugin" to call it globaly directly from $.myPlugin();
        var defaults = {
            target: ".box",
            buttons: "li a"             
        };

        options = $.extend(defaults, options);

        function logic(){
            // ... code goes here
        }

        //DEFINE WHEN TO RUN THIS PLUGIN
        $(window).on('load resize', function () { // Load and resize as example ... use whatever you like
            logic();
        });

        // RETURN OBJECT FOR CHAINING
        // return this;

        // OR FOR FOR MULTIPLE OBJECTS
        // return this.each(function() {
        //    // Your code ...
        // });

    };
})(jQuery);


// USE EXAMPLE with default settings
$.myPlugin(); // or run plugin with default settings like so.

// USE EXAMPLE with overwriten settings
var options = {
    target: "div.box", // define custom options
    buttons: ".something li a" // define custom options
}     
$.myPlugin(options); //or run plugin with overwriten default settings