admin管理员组

文章数量:1025270

How can I extend a plugin's public methods its prototype?

For instance, I have method1 in my plugin, and I want to add another and more through its .prototype. Is it possible?

var extensionMethods = {
   method2: function(){
        return this;
    }
};

$.fn.MyPlugin.prototype = extensionMethods;

console.log($(".element").MyPlugin());

result,

 Object { Element={...}, Options={...}, method1=function()}

Ideally,

 Object { Element={...}, Options={...}, method1=function(), method2=function(), method2function()}

my plugin boilerplate,

(function ($) {

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(PublicOptions) {

        // Set private defaults.
        var Defaults = {
            param1:       'param1',
            param2:       'param2',
            onSuccess:    function(){}
        };

        // Do a deep copy of the options.
        var Options = $.extend(true, {}, Defaults, PublicOptions);

        // Define a functional object to hold the api.
        var PluginApi = function(Element, Options) {
            this.Element   = Element;
            this.Options   = Options;
        };

        // Define the public api and its public methods.
        PluginApi.prototype = {

            method1: function(PublicOptions) {

                // Process the options.
                var Options = $.extend(true, {}, this.Options, PublicOptions);
                return this.Options;
            }
        };

        //Create a new object of api.
        return new PluginApi(this, Options);
    };

})(jQuery);

Any ideas?

How can I extend a plugin's public methods its prototype?

For instance, I have method1 in my plugin, and I want to add another and more through its .prototype. Is it possible?

var extensionMethods = {
   method2: function(){
        return this;
    }
};

$.fn.MyPlugin.prototype = extensionMethods;

console.log($(".element").MyPlugin());

result,

 Object { Element={...}, Options={...}, method1=function()}

Ideally,

 Object { Element={...}, Options={...}, method1=function(), method2=function(), method2function()}

my plugin boilerplate,

(function ($) {

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(PublicOptions) {

        // Set private defaults.
        var Defaults = {
            param1:       'param1',
            param2:       'param2',
            onSuccess:    function(){}
        };

        // Do a deep copy of the options.
        var Options = $.extend(true, {}, Defaults, PublicOptions);

        // Define a functional object to hold the api.
        var PluginApi = function(Element, Options) {
            this.Element   = Element;
            this.Options   = Options;
        };

        // Define the public api and its public methods.
        PluginApi.prototype = {

            method1: function(PublicOptions) {

                // Process the options.
                var Options = $.extend(true, {}, this.Options, PublicOptions);
                return this.Options;
            }
        };

        //Create a new object of api.
        return new PluginApi(this, Options);
    };

})(jQuery);

Any ideas?

Share Improve this question asked Jan 11, 2015 at 15:32 RunRun 57.4k178 gold badges464 silver badges771 bronze badges 12
  • According to your structure (pretty unfortunate, actually, because this is not jQuery plugin) you can only access PluginApi.prototype via some object returned $(".element").MyPlugin() because PluginApi constructor is local inside IIFE. But this is of course not very good. – dfsq Commented Jan 11, 2015 at 15:39
  • How can I change the structure to allow that then? – Run Commented Jan 11, 2015 at 15:40
  • most jquery plugins return this to maintain jQuery chainability. Why are you so set on wanting to create public methods on prototype? – charlietfl Commented Jan 11, 2015 at 15:40
  • @charlietfl can you please provide an example for chainability? – Run Commented Jan 11, 2015 at 15:46
  • 1 it wraps all the plugin code and insulates it (closure) as well as provides context for $ so plugin won't break on page where other libraries use $ – charlietfl Commented Jan 11, 2015 at 16:28
 |  Show 7 more ments

2 Answers 2

Reset to default 2

I think the best structure you can do in this case would not involve prototypes at all. Check this plugin base:

(function($) {

   // Set private defaults.
    var Defaults = {
        param1: 'param1',
        param2: 'param2',
        onSuccess: function() {}
    };

    // Define the public api and its public methods.
    var PluginApi = {

        extend: function(name, method) {
            PluginApi[name] = method;
            return this;
        },

        init: function(PublicOptions) {

            // Do a deep copy of the options.
            var Options = $.extend(true, {}, Defaults, PublicOptions);

            return this.each(function() {
                console.log('set up plugin logic', this.tagName);
            });
        },

        method1: function() {
            console.log('called: method1');
            return this;
        }
    };

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(method) {

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

})(jQuery);

This plugin structure allows you to chain methods as expected:

$('h1').MyPlugin('method1').css('color', 'red');

In case of the need to use non-existent method you could do this:

// Extend plugin "prototype" with method2 and use it
$('h1, h2').MyPlugin('extend', 'method2', function(prop, value) {
    return this.css(prop, value);
}).MyPlugin('method2', 'color', 'green');

Check usage example in the demo below.

(function($) {

   // Set private defaults.
    var Defaults = {
        param1: 'param1',
        param2: 'param2',
        onSuccess: function() {}
    };

    // Define the public api and its public methods.
    var PluginApi = {

        extend: function(name, method) {
            PluginApi[name] = method;
            return this;
        },

        init: function(PublicOptions) {
            
            // Do a deep copy of the options.
            var Options = $.extend(true, {}, Defaults, PublicOptions);
            
            return this.each(function() {
                console.log('set up plugin logic', this.tagName);
            });
        },

        method1: function() {
            console.log('called: method1');
            return this;
        }
    };

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(method) {

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

})(jQuery);


// Call existen method1: should make h1 and h2 red
$('h1, h2').MyPlugin('method1').css('color', 'red');

// Call non-existent method2: should throw error in console
try {
    $('h1, h2').MyPlugin('method2').css('color', 'green');
}
catch (e) {
  // Extend "plugin" prototype with method2
  $('h1, h2').MyPlugin('extend', 'method2', function(prop, value) {
      return this.css(prop, value);
  }).MyPlugin('method2', 'color', 'green');
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>H1</h1>
<h2>H2</h2>

Or it may be more optimal to define a static method extend within $[pluginName] namespace:

// Attach the plugin to jQuery namespace.
$.fn[pluginName] = function(method) {

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

$[pluginName] = {};
$[pluginName].extend = function(name, method) {
    PluginApi[name] = method;
};

and then use it like this when necessary to add additional methods:

$.MyPlugin.extend('method2', function(prop, value) {
    return this.css(prop, value);
});

$('h1, h2').MyPlugin('method2', 'color', 'green');

Final demo: http://plnkr.co/edit/qqlfRqAM84goscU5BFNU?p=preview

You can't extend the prototype outside because you use hidden object PluginApi.

You can try to store PluginApi outside of a plugin function:

$[pluginName] = function(Element, Options) {
    this.Element   = Element;
    this.Options   = Options;
};
$[pluginName].prototype = {
    method1: function(PublicOptions) {
        // Process the options.
        var Options = $.extend(true, {}, this.Options, PublicOptions);
        return this.Options;
    }
};

$.fn[pluginName] = function(PublicOptions) {
    // Set private defaults.
    var Defaults = {
        param1:       'param1',
        param2:       'param2',
        onSuccess:    function(){}
    };

    // Do a deep copy of the options.
    var Options = $.extend(true, {}, Defaults, PublicOptions);

    return new $[pluginName](this, Options);
};

and then you can extend the the prototype:

$.MyPlugin.prototype.method2 = function() {
    return this;
}

How can I extend a plugin's public methods its prototype?

For instance, I have method1 in my plugin, and I want to add another and more through its .prototype. Is it possible?

var extensionMethods = {
   method2: function(){
        return this;
    }
};

$.fn.MyPlugin.prototype = extensionMethods;

console.log($(".element").MyPlugin());

result,

 Object { Element={...}, Options={...}, method1=function()}

Ideally,

 Object { Element={...}, Options={...}, method1=function(), method2=function(), method2function()}

my plugin boilerplate,

(function ($) {

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(PublicOptions) {

        // Set private defaults.
        var Defaults = {
            param1:       'param1',
            param2:       'param2',
            onSuccess:    function(){}
        };

        // Do a deep copy of the options.
        var Options = $.extend(true, {}, Defaults, PublicOptions);

        // Define a functional object to hold the api.
        var PluginApi = function(Element, Options) {
            this.Element   = Element;
            this.Options   = Options;
        };

        // Define the public api and its public methods.
        PluginApi.prototype = {

            method1: function(PublicOptions) {

                // Process the options.
                var Options = $.extend(true, {}, this.Options, PublicOptions);
                return this.Options;
            }
        };

        //Create a new object of api.
        return new PluginApi(this, Options);
    };

})(jQuery);

Any ideas?

How can I extend a plugin's public methods its prototype?

For instance, I have method1 in my plugin, and I want to add another and more through its .prototype. Is it possible?

var extensionMethods = {
   method2: function(){
        return this;
    }
};

$.fn.MyPlugin.prototype = extensionMethods;

console.log($(".element").MyPlugin());

result,

 Object { Element={...}, Options={...}, method1=function()}

Ideally,

 Object { Element={...}, Options={...}, method1=function(), method2=function(), method2function()}

my plugin boilerplate,

(function ($) {

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(PublicOptions) {

        // Set private defaults.
        var Defaults = {
            param1:       'param1',
            param2:       'param2',
            onSuccess:    function(){}
        };

        // Do a deep copy of the options.
        var Options = $.extend(true, {}, Defaults, PublicOptions);

        // Define a functional object to hold the api.
        var PluginApi = function(Element, Options) {
            this.Element   = Element;
            this.Options   = Options;
        };

        // Define the public api and its public methods.
        PluginApi.prototype = {

            method1: function(PublicOptions) {

                // Process the options.
                var Options = $.extend(true, {}, this.Options, PublicOptions);
                return this.Options;
            }
        };

        //Create a new object of api.
        return new PluginApi(this, Options);
    };

})(jQuery);

Any ideas?

Share Improve this question asked Jan 11, 2015 at 15:32 RunRun 57.4k178 gold badges464 silver badges771 bronze badges 12
  • According to your structure (pretty unfortunate, actually, because this is not jQuery plugin) you can only access PluginApi.prototype via some object returned $(".element").MyPlugin() because PluginApi constructor is local inside IIFE. But this is of course not very good. – dfsq Commented Jan 11, 2015 at 15:39
  • How can I change the structure to allow that then? – Run Commented Jan 11, 2015 at 15:40
  • most jquery plugins return this to maintain jQuery chainability. Why are you so set on wanting to create public methods on prototype? – charlietfl Commented Jan 11, 2015 at 15:40
  • @charlietfl can you please provide an example for chainability? – Run Commented Jan 11, 2015 at 15:46
  • 1 it wraps all the plugin code and insulates it (closure) as well as provides context for $ so plugin won't break on page where other libraries use $ – charlietfl Commented Jan 11, 2015 at 16:28
 |  Show 7 more ments

2 Answers 2

Reset to default 2

I think the best structure you can do in this case would not involve prototypes at all. Check this plugin base:

(function($) {

   // Set private defaults.
    var Defaults = {
        param1: 'param1',
        param2: 'param2',
        onSuccess: function() {}
    };

    // Define the public api and its public methods.
    var PluginApi = {

        extend: function(name, method) {
            PluginApi[name] = method;
            return this;
        },

        init: function(PublicOptions) {

            // Do a deep copy of the options.
            var Options = $.extend(true, {}, Defaults, PublicOptions);

            return this.each(function() {
                console.log('set up plugin logic', this.tagName);
            });
        },

        method1: function() {
            console.log('called: method1');
            return this;
        }
    };

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(method) {

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

})(jQuery);

This plugin structure allows you to chain methods as expected:

$('h1').MyPlugin('method1').css('color', 'red');

In case of the need to use non-existent method you could do this:

// Extend plugin "prototype" with method2 and use it
$('h1, h2').MyPlugin('extend', 'method2', function(prop, value) {
    return this.css(prop, value);
}).MyPlugin('method2', 'color', 'green');

Check usage example in the demo below.

(function($) {

   // Set private defaults.
    var Defaults = {
        param1: 'param1',
        param2: 'param2',
        onSuccess: function() {}
    };

    // Define the public api and its public methods.
    var PluginApi = {

        extend: function(name, method) {
            PluginApi[name] = method;
            return this;
        },

        init: function(PublicOptions) {
            
            // Do a deep copy of the options.
            var Options = $.extend(true, {}, Defaults, PublicOptions);
            
            return this.each(function() {
                console.log('set up plugin logic', this.tagName);
            });
        },

        method1: function() {
            console.log('called: method1');
            return this;
        }
    };

    // Create the plugin name and defaults once
    var pluginName = 'MyPlugin';

    // Attach the plugin to jQuery namespace.
    $.fn[pluginName] = function(method) {

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

})(jQuery);


// Call existen method1: should make h1 and h2 red
$('h1, h2').MyPlugin('method1').css('color', 'red');

// Call non-existent method2: should throw error in console
try {
    $('h1, h2').MyPlugin('method2').css('color', 'green');
}
catch (e) {
  // Extend "plugin" prototype with method2
  $('h1, h2').MyPlugin('extend', 'method2', function(prop, value) {
      return this.css(prop, value);
  }).MyPlugin('method2', 'color', 'green');
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>H1</h1>
<h2>H2</h2>

Or it may be more optimal to define a static method extend within $[pluginName] namespace:

// Attach the plugin to jQuery namespace.
$.fn[pluginName] = function(method) {

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

$[pluginName] = {};
$[pluginName].extend = function(name, method) {
    PluginApi[name] = method;
};

and then use it like this when necessary to add additional methods:

$.MyPlugin.extend('method2', function(prop, value) {
    return this.css(prop, value);
});

$('h1, h2').MyPlugin('method2', 'color', 'green');

Final demo: http://plnkr.co/edit/qqlfRqAM84goscU5BFNU?p=preview

You can't extend the prototype outside because you use hidden object PluginApi.

You can try to store PluginApi outside of a plugin function:

$[pluginName] = function(Element, Options) {
    this.Element   = Element;
    this.Options   = Options;
};
$[pluginName].prototype = {
    method1: function(PublicOptions) {
        // Process the options.
        var Options = $.extend(true, {}, this.Options, PublicOptions);
        return this.Options;
    }
};

$.fn[pluginName] = function(PublicOptions) {
    // Set private defaults.
    var Defaults = {
        param1:       'param1',
        param2:       'param2',
        onSuccess:    function(){}
    };

    // Do a deep copy of the options.
    var Options = $.extend(true, {}, Defaults, PublicOptions);

    return new $[pluginName](this, Options);
};

and then you can extend the the prototype:

$.MyPlugin.prototype.method2 = function() {
    return this;
}

本文标签: javascriptHow to extend a jquery plugin39s public methods through its prototypeStack Overflow