创建jQuery插件的最优雅的方法是什么?

5
我只想知道这三种方法中哪一种最好,可能还要知道为什么?
1)
(function($) {
    var methods = {
        init    : function(options) {
            if( this.length ){
                var settings = {
                    something   : 'fast'
                };
                return this.each(function(){
                    if ( options ) {
                        $.extend( settings, options );
                    }

                    var $el         = $(this),

                    /* some variables */
                        $selector1  = $el.find('container'),
                        count       = $selector1.length,
                        $selector2  = $el.find('pictures');

                    /* do something e.g.*/
                    doSomething1();
                    doSomething2();

                    function doSomething1() {
                        $selector1.css('width' , '300px');
                    };

                    function doSomething2() {
                        $selector2.each(function(i) {
                            //do something
                        });
                    };  
                });
            }
        },
        update  : function() { },
        destroy : function() { }
    };

    $.fn.pluginName = function(method) {
        if ( methods[method] ) {
            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  method + ' does not exist on jQuery.pluginName' );
        }
    };
})(jQuery);

2)

(function($) {
    var internal= {
        doSomething1    : function($selector1) {
            $selector1.css('width' , '300px');
        },
        doSomething1    : function($selector2) {
            $selector2.each(function(i) {
                //do something
            });
        }
    };

    var methods = {
        init    : function(options) {
            if( this.length ){
                var settings = {
                    something   : 'fast'
                };
                return this.each(function(){
                    if ( options ) {
                        $.extend( settings, options );
                    }

                    /* 
                    The variables are declared here
                    other methods are declared in the internal obj literal
                    */
                    var $el         = $(this),

                    /* some variables */
                        $selector1  = $el.find('container'),
                        count       = $selector1.length,
                        $selector2  = $el.find('pictures');

                    /* do something e.g.*/
                    internal.doSomething1($selector1);
                    internal.doSomething2($selector2);
                });
            }
        },
        update  : function() { },
        destroy : function() { }
    };

    $.fn.pluginName = function(method) {
        if ( methods[method] ) {
            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  method + ' does not exist on jQuery.pluginName' );
        }
    };
})(jQuery);

3)
(function($) {
    /* 
    The variables are declared here
    */
    var $el,
        $selector1,
        count,
        $selector2;

    var internal= {
        doSomething1    : function() {
            $selector1.css('width' , '300px');
        },
        doSomething1    : function() {
            $selector2.each(function(i) {
                //do something
            });
        }
    };

    var methods = {
        init    : function(options) {
            if( this.length ){
                var settings = {
                    something   : 'fast'
                };
                return this.each(function(){
                    if ( options ) {
                        $.extend( settings, options );
                    }

                    /* 
                    The variables are set here
                    other methods are declared in the internal obj literal
                    */
                    $el         = $(this),
                    $selector1  = $el.find('container'),
                    count       = $selector1.length,
                    $selector2  = $el.find('pictures');

                    /* do something e.g.*/
                    internal.doSomething1();
                    internal.doSomething2();
                });
            }
        },
        update  : function() { },
        destroy : function() { }
    };

    $.fn.pluginName = function(method) {
        if ( methods[method] ) {
            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  method + ' does not exist on jQuery.pluginName' );
        }
    };
})(jQuery);
1个回答

7

2) 是正确的答案。

对于1),每次调用.pluginName时创建两个函数的新实例是没有必要的,因为它们是静态的。

对于3),我认为将那些本地.pluginName调用特定变量作为你的插件的静态变量是不好的做法。它现在可能不会导致错误,但很可能会引起微妙的错误。

基本上,init函数内的所有内容都应该是特定于调用的内容,而(function($){...})闭包内的所有内容都应该是插件的静态内容。

你尝试过4)吗?

(function($) {
    var MyObject = function($el) {
        /* 
        The variables are declared here
        other methods are declared in the internal obj literal
        */
        /* some variables */
        this.$selector1  = $el.find('container');
        var count       = $selector1.length;
        this.$selector2  = $el.find('pictures');

        /* do something e.g.*/
        this.doSomething1($selector1);
        this.doSomething2($selector2);
    };

    MyObject.prototype.doSomething1 = function() {
       this.$selector1.css('width' , '300px');
    };
    MyObject.prototype.doSomething2 = function() {
       this.$selector2.each(function(i) {
           //do something
       });
    };

    var methods = {
        init    : function(options) {
            if( this.length ){
                var settings = {
                    something   : 'fast'
                };
                return this.each(function(){
                    if ( options ) {
                        $.extend( settings, options );
                    }

                    new MyObject($(this));
                });
            }
        },
        update  : function() { },
        destroy : function() { }
    };

    $.fn.pluginName = function(method) {
        if ( methods[method] ) {
            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  method + ' does not exist on jQuery.pluginName' );
        }
    };
})(jQuery);

作为一般评论,API设计中使用$(obj).pluginName("method", ...)可能会让人感到烦恼/不直观。
我建议您也可以使用$(obj).pluginName().method(...),以保持jQuery链式调用的特性。

我的偏好和我目前正在使用的也是(2)。然而,我有一个带有许多变量的插件,我需要将它们传递给我的函数。这可能会变得非常烦人,例如:internal.doSomething1(var1,var2,var3,var4); - Ricardo
请注意,第二个问题中我忘记在函数调用中传递变量了:S 应该是 internal.doSomething1($selector1); internal.doSomething2($selector2); - Ricardo
在这种情况下,您的 doSomething(...) 应该接受一个设置对象。 - Keith Rousseau
@Ricardo 为什么不将 $el 传递给函数,让函数获取选择器/其他变量等呢? - Raynos
@Ricardo 是的,那样做不好。你考虑过使用一个对象来完成这个任务吗?请参见答案的编辑部分。 - Raynos
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接