jQuery 插件,包含属于给定插件实例的"local"变量



我正在开发一个jQuery插件,但遇到了变量"范围"的问题。每个插件都需要跟踪一个相当大的多维数组,以及jQuery插件所附加的根元素。

如下所示,我在插件代码的顶部定义了var $rootEl = null;var multidArray = null;;然而,当我在多个元素上运行$(anySelector).pluginName("value");时(或者当我用不同的选择器调用.pluginName()两次时),这些变量似乎不是沙盒/作用域到插件实例的,因此第二个实例覆盖了这两个值,并且第一个实例在DOM中看起来是空白的(因为所有jQuery操作都应用在$rootEl上,原始值在第二个jQuery插件调用上被覆盖)。

(function ($) {
var $rootEl = null;
var multidArray = null;
directChildren = function(uuid) {
return $rootEl.children("[data-uuid=" + uuid + "]");
},
incrementCounter = function() {
this.counter++;
},
resetGenIteration = function() {
this.counter = 0;
},
process = function(item) { // Will be called from a callback(val) in another
// javascript file but still needs to have access to the local variables
// of the current jQuery plugin
incrementCounter();
...
},
...
$.fn.pluginName = function(rootvar) {
this.each(function() {
$rootEl = $(this);
resetGenIteration();
populate(rootvar);
$rootEl.addClass("pluginName").delegate("div", "click", divClick);
});
}
}(jQuery));

昨天,我试图将插件转换为使用init和this.varname来存储值,对于插件的每个实例,它似乎都正确地保留了this.$rootEl属性,但我必须将所有函数移到var PluginName = { ... }中,并将它们从funcName = function()切换到funcName: function(),才能看到插件实例的"本地"变量。然后,我遇到了许多没有定义函数的错误,所以我尝试在所有函数调用前加上this.PluginName.,分别读作this.functionName()PluginName.functionName(),这对某些函数有效,但对所有函数无效。我的一些函数是从另一个jQuery插件调用的,该插件运行回调函数并将数据传递给它,在这些情况下,this.$rootEl变为undefined

(function ($){
var PluginName = {
init: function(options, rootEl) {
this.options = $.extend({}, this.options, options);
this.rootEl  = rootEl;
this.$rootEl = $(rootEl);
this.setListeners();
return this;
},
options: {
rootvar: "test"
},
...
setListeners:function (){
var self = this;
this.$rootEl.on("div", "click", function () {
$.proxy(self.divClick, self);
});
}
};
...
$.fn.pluginName = function(options) {
this.init = function(options, rootEl) {
this.options = $.extend({}, this.options, options);
this.rootEl  = rootEl;
this.$rootEl = $(rootEl);
this.multidArray = null;

return this;
};
this.each(function() {
var plugInstance = Object.create(PluginName);
plugInstance.init(options, this);
$.data(this, 'pluginName', plugInstance);
});
}
}(jQuery));

如何重构代码以存储多维数组和根元素,而只有给定的插件才能在内部检索和修改?我肯定不应该将.data()用于大型阵列吗?

谢谢!

插件只是一个附加到jQuery对象的函数。它没有多个实例,但可以多次调用。每个调用都会创建一个新的作用域,但是,在pluginName函数之外声明的变量将由每个调用共享。

与您在第二次迭代中尝试的面向对象方法不同,我建议重新使用多个函数。但是,您可以向这些函数传递所有必需的参数,这样它们就不必依赖于不明确的this值。

由于您只包含了代码的一部分片段,我无法清楚地了解您想要实现什么,但我在下面包含了一个示例插件,它突出了如何正确使用scope。

(function ($) {
// declare variables that can be shared by every call of "pluginName" here
var defaultColor = 'green'
// passing the element as an argument means we don't have to rely on ambiguous 'this'.
function someTransformation ($el, someColor) {
return $el.css({backgroundColor: someColor})
}
$.fn.pluginName = function (color, idx) {
// declare variables that should be reinitialized on every call of "pluginName" here
var els = []
this.each(function () {
var $el = $(this)
someTransformation($el, defaultColor)
els.push($el)
})
// we can still use the array we stored
if (els[idx]) {
someTransformation(els[idx], color)
}
}
})($)
$('#first i').pluginName('red', 2)
$('#second i').pluginName('blue', 0)
i {
display: inline-block;
width: 50px;
height: 50px;
margin: 10px;
}
div {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="first">
<i></i><i></i><i></i><i></i>
</div>
<div id="second">
<i></i><i></i><i></i><i></i>
</div>

如果您仍然有问题,请从执行pluginName函数中的所有开始。而不声明额外的助手。这将帮助您确保所有逻辑都正常工作。然后开始将任何重用的代码移动到接受运行所需的所有参数的辅助函数中。

一些相关但没有立即帮助的挑剔:pluginName(例如directChildren)之外的函数表达式是全局表达式。请确保在var之前为它们做序,以使它们在IIFE的范围内。

正如@Damon所说,jquery插件没有什么特别之处——它只是一个函数,JS就是关于函数的;)

所以你所有的问题都是因为没有仔细考虑你所有的用法。我不知道你有(或计划有)什么用法,但下面是我的做法。这是一个为每个实例存储"bigArray"的插件。将其递增并放入对象html中。以下解释:

(function($) {
let _plugInstances = [];
function PlugFactory(options) {
let _plugInstances = [],
_create = function(options) {
let $el = $(this),
id = $el.data('PlugID') || (+new Date +''+Math.random()).replace('.', '_');
if (_plugInstances[id])
return _plugInstances[id];
$el.data('PlugID', id);
_plugInstances[id] = new Plug($el, options);
return _plugInstances[id];
};
this.instantiate = function(options) {
let newPlugs = this.map(_create);
return newPlugs.length > 1 ? newPlugs : newPlugs[0];
}
}
function Plug(rootEl, options) {
let _$rootEl = rootEl,
_options = options,
_bigArr = [];
this.add = (item) => {
_bigArr.push(item);
return this;
};
this.refresh = () => {
_$rootEl.html(_$rootEl.data('PlugID') + ":" + _bigArr.join('-'));
return this;
}
}
$.extend({plug: new PlugFactory});
// extend plugin scope
$.fn.extend({
plug: $.plug.instantiate
});
})(jQuery);
// Usage
$(document).ready(function () {
$('div').plug({opt: 'whatever'});
$('div').on('click', function() {
$(this).plug()
.add(1)
.refresh();
})
$('#i4').plug({opt: 'whatever'}).add(2).refresh();
});
div{border: solid 2px gray;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>1</div><div>2</div><div>3</div>
<br/>
<div id="i4">4</div>

解释:

正如您所看到的,您在$.fn.pluginName中使用的东西被移到了单独的函数(比如类)(PluginFactory)

它实际上是存储插件实例的函数。

正如您可能注意到的,有一些变量从下划线开始——它们是私有属性——问题是它们是闭包,因此它们只在定义它们的函数内部可见,并且由于它们是变量,因此不能作为函数实例属性访问。

此外,请考虑ES6"上下文安全"的东西,比如短函数表示法和let。

现在是实例的问题

正如您所看到的,我们为每个元素生成了一些UID(您可以使用任何其他生成器,这只是一个POC)

接下来,我们用新的运营商创建新的实例

并将其保存到Map中,以简化获取实例的过程。

并将我们的UID保存在元素jquery数据(PliginID)中,现在我们知道我们的元素是否已经有了插件的实例。

现在是主要部分(插件功能)

这实际上是你的实例,它将贯穿于".plug".的整个调用过程

每次获得$('#element').plug()时,您都会收到最初创建的实例。

因此,在那里你可以访问所有的私人成员,并设计你的公共方法。

PS。我并不是说它是最好的,只是肯定是其中之一,作为如何解决任务的一个例子,"以一种只有给定插件才能内部检索和修改的方式存储多维数组和根元素">

希望它足够笼统,有帮助;)干杯

最新更新