jQuery插件开发如何保持变量和方法的私密性

时间:2015-06-17 15:27:38

标签: javascript jquery-plugins prototypal-inheritance private-members

出于练习目的,我正在创建一个jQuery插件,一个简单的图像滑块。我使用Boilerplate - jQuery Plugins中的模式。

在初始化过程中,一切都按预期工作,每个实例都获得设置所需的正确值(宽度和高度,也包括事件绑定)。

当我尝试将计算出的slide-width传递给执行动画的函数(单击下一个按钮)时,麻烦就开始了。我试图保存的每个值都被最后一个实例覆盖 - 好吧,这就是我所学习的原型继承所做的事情。

我搜索了很多,并且在库存溢出中找到了(不仅仅是)这个解决方案:global or local variables in a jquery-plugin。接受的答案建议存储HTML标记中需要私有的值,data-whatever属性。

这非常好,但不是很好。

第二个答案似乎更优雅,将真实的私有变量保留在每个实例的范围内:

(function($) {

$.pluginName = function(element, options) {
....
var plugin = this;
....
        // a public method
    plugin.foo_public_method = function() {
          ....
    }

    // private methods
    var foo_private_method = function() {
          ....
    }
    plugin.init();

} ....

看起来非常简单 - 但我无法实现此解决方案!

所以我的问题是:如何在以下插件模式中实现私有变量?(如何从Plugin.prototype中正确调用它...)

我用我的插件创建了一个Fiddle。代码摘要:

(function ($, window, document, undefined) {

var pluginName = "easyCarousel";

// default configuration object
defaults = {
       ... default values here, css etc
    }
};

// The actual plugin constructor
function Plugin(element, options) {

    this.element = element;
    this.options = $.extend({}, defaults, options);
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
}

Plugin.prototype = {

    init: function () {

        var options = this.options;
        this.options.stage = $(this.element);
        .... calling/starting views and controllers

    },

    buildCollectionView: function (options, Helper) {
         .... setting markup, applying css, calulating dimensions 
    },

    buildStageView: function (options, Helper) {

        .... setting markup, applying css
        .... storing calculated slide width in data Attribute

        stage.attr({
            'data-slidewidth': width,
            'data-stagewidth': setWidth
        });
    },

    buildTheatreView: function (options, Helper) {

        .... setting markup, applying css

    },

    buildNavigationView: function (options) {

        .... setting markup

    },


    navigationController: 

    function (options, slideForward, slideBackward) {

        .... event binding

        pubSub.on("navigation:arrownext:click", function (e) {
            slideForward(options, e);
        });

        $('.carousel__arrownext', options.theatre)
        .bind('click', function () {
            pubSub.trigger("navigation:arrownext:click", this);
        });
    },

    slideForwardAnimation: function (options, e) {

       .... reading stored width value from data Attribute

        $element.animate({
            "left": "-=" + slideWidth
        })
    },


    setDimensionsHelper: function (options) {

        .... calculating an returning dimensions of the slider
    },

    eventBusHelper: function (options) {
        // integrating third party eventbus
    }
};

$.fn[pluginName] = function (options) {

    return this.each(function () {
        if (!$.data(this, "plugin_" + pluginName)) {
            $.data(this, "plugin_" + pluginName,
            new Plugin(this, options));
        }
    });    
 }})(jQuery, window, document);

我希望我的代码示例不会复杂/长时间查看。

正如您所看到的,我对高级Javascript模式非常陌生 - 而且现在我真的卡住了。我已经花了很多时间来解决这个问题。因此,非常感谢每一次提供帮助的尝试。当然还有关于代码的每一次评论。

提前致谢:)

1 个答案:

答案 0 :(得分:3)

您需要做的就是在原型中定义变量(直接或在原型中的方法内),而不是在构造函数中进行。构造函数中定义的变量将在插件的所有实例之间共享,而原型中定义的变量将是每个实例。

另外,你没有" var"您的"默认值"前面的关键字定义,所以你实际上创建了一个名为"默认值"的全局变量。而不是仅限于你关闭的那个。

这是我建议的:

// default configuration object
var defaults = {
       ... default values here, css etc
    }
};

// The actual plugin constructor
function Plugin(element, options) {
    this._defaults = defaults;
    this._name = pluginName;
    this.init(element, options);
}

Plugin.prototype = {

    init: function (element, options) {
        this.element = element;
        this.options = $.extend({}, this._defaults, options);
        this.options.stage = $(this.element);
        .... calling/starting views and controllers

    },

甚至更好:

// The actual plugin constructor
function Plugin(element, options) {
    this.init(element, options);
}

Plugin.prototype = {

    _name: 'put your name here',

    _defaults: {
        // just define them here, no need for an outside variable
    },

    init: function (element, options) {
        this.element = element;
        this.options = $.extend({}, this._defaults, options);

        // why are you saving stage as an option? That doesn't make sense.
        this.options.stage = $(this.element);
        // Makes more sense to just do this ($el is a more standard name for this)
        this.$el = $(this.element);
        // .... calling/starting views and controllers

    },