数组作为 JQuery Widget 实例变量

问题描述 投票:0回答:2

首先 - 我对 JQuery 小部件没有完全了解。我将它们视为可以自行保存状态和表示的对象。我用于设计的示例来自微软的“SILK”项目。

问题 - 由于我正忙于为新站点创建视觉元素作为 JQuery 小部件,一切都很顺利,直到我使用小部件初始化了超过 1 个元素。与其他所有变量相比,实例变量(数组)似乎是全局的。所以我创建了一个简单的测试项目来演示该行为:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <script type="text/javascript" src="jquery-1.10.2.min.js"></script>
    <script type="text/javascript" src="jquery-ui-1.9.2.min.js"></script>
    <script type="text/javascript">
        (function ($, undefined) {
            $.widget('AR.MultiQueue', {
                options: {
                    queueName: "",
                    Names: [],
                    Wrong: [],
                    Right: []
                },

                _create: function () {
                    var temp = [];
                    for (var i = 0; i < this.options.Names.length; i++) {
                        console.log('pushing ' + this.options.Names[i] + ' for ' + this.options.queueName);
                        this.options.Wrong.push(this.options.Names[i]);
                        temp.push(this.options.Names[i]);
                    }

                    this.options.Right = temp;
                },

                Test: function () {
                    console.log(this.options.queueName);
                    for (var i = 0; i < this.options.Names.length; i++)
                        console.log('Name->' + this.options.Names[i]);
                    for (var i = 0; i < this.options.Wrong.length; i++)
                        console.log('Wrong->' + this.options.Wrong[i]);
                    for (var i = 0; i < this.options.Right.length; i++)
                        console.log('Right->' + this.options.Right[i]);
                },

                destroy: function () {
                    $.Widget.prototype.destroy.apply(this, arguments);
                }
            });
        }(jQuery));

    </script>
</head>
<body>
<div id="a"></div>
    <div id="b"></div>
    <script type="text/javascript">
        (function () {
            $('#a').MultiQueue({ queueName: 'a', Names: ["a"] });
            $('#b').MultiQueue({ queueName: 'b', Names: ["b"] });
            setTimeout(function () {
                $('#a').MultiQueue('Test');
                console.log('Next widget');
                $('#b').MultiQueue('Test');
            }, 1000);
        })();


    </script>
</body>
</html>

因此,我期望的输出是每个小部件仅打印相应的(“a”或“b”)数组字符串3次。相反,输出是:

pushing a for a
pushing b for b
a
Name->a
Wrong->a
Wrong->b
Right->a
Next widget
b
Name->b
Wrong->a
Wrong->b
Right->b

如您所见,两个小部件都为使用直接实例变量“push”的数组打印出“a”和“b”。现在我只是重写我的小部件以使用临时数组对象,例如示例中的“Right”选项var,但我感觉 jQuery 小部件还有更多我能理解的内容(例如,我是否像我一样编写代码)应该???)

我在网上搜索了这个,但我唯一能想到的是
- 不要使用 jQuery 插件作为实例
- jQuery 小部件存储在数组中
- 事实上我从来没有真正理解 javascript 中的数组(因为事实上数组具有固定大小,最接近的是 C# 中的 List<>)。

无论如何,我真的很想知道为什么会发生这种情况(如果我能期待更多这样的惊喜,这需要一些时间来找出)

谢谢

javascript jquery arrays widget
2个回答
0
投票

在选项中使用

null
而不是
[]
,并且仅在
_create
_init
上启动它们。发生这种情况是因为该数组针对该小部件类型(基本上来说)实例化一次,并保留作为创建的每个新小部件的引用。原始类型被复制(递归),但数组被引用,因此当 jQuery 复制选项时,数组保持不变。


0
投票

IMO 这是一个错误,当我发现它时,我在这里提出了它: https://github.com/jquery/jquery-ui/issues/2300

我知道已经过去了 10 年,但我想我终于可以回答你的问题了:“为什么”是这样。与许多错误一样,这是优化工作的结果。 如果小部件的输入是一个数组,它很可能很大,因此复制该数组的成本可能非常高。这就是为什么他们不复制它。参见这里:

https://github.com/jquery/jquery-ui/pull/193

问题在于,处理此问题的代码当前不知道什么是输入数组以及什么是原型/默认数组。可以合理地说,您不应该复制输入数组,因为它可能成本高昂,但在我看来,同样合理的是,您绝对“应该”复制原型数组,因为不这样做几乎肯定会有害。您发现的问题意味着有一个可选的静态选项,在所有小部件实例之间共享,一旦有人提供该选项,该选项就不再是静态的。

正如接受的答案所述,您可以通过将该选项设置为 null 并稍后初始化该选项来避免这种情况(前提是尚未提供该选项),但这意味着您已经模糊了代码的键入并向每个小部件添加了样板代码检查它是否确实已被分配,并记住每次都这样做。我宁愿不。 我很幸运,因为我有一个父小部件,我可以在其中添加对其

_create

函数中的解决方案的调用,从而为我提供了一个集中式解决方案。这是非常幼稚的,如果你有一个多维数组原型,它就不会工作,尽管如果你曾经有过这样一个令人发指的原型,它可以很容易地适应它。

_cloneOptionsArrays: function _cloneOptionsArrays(protoObj, optionObj) { protoObj = protoObj || this.__proto__.options; optionObj = optionObj || this.options; let key; for (key in optionObj) { const value = optionObj[key]; const protoValue = protoObj[key]; if ($.isPlainObject(value) && protoValue) { that._cloneOptionsArrays(protoValue, value); } else if (Array.isArray(value) && protoValue === value) { optionObj[key] = [].concat(value); } } },

这与现有的 jquery ui 代码类似,递归地导航简单对象,但是当它找到一个数组而不是接受当前引用时,它首先检查它是否是原型引用,如果是,则创建一个新副本。重新阅读它,我刚刚意识到它也可能调用 

[].slice()
 而不是 
[].concat(value)

,但我怀疑当你几乎总是处理空数组时它会有多大区别

    

© www.soinside.com 2019 - 2024. All rights reserved.