我正在尝试使用现有的工作代码库,并使用JavaScript使其面向对象。我的系统在一对多关系中使用包含 groups 和 items 的JSON,并在页面上显示此内容。这些项目可以移动到不同的组中,并且还需要计算它们在这些组中的位置。因此,需要建立能够了解周围群组和门票的事件。
我正在使用John Resig's简单的JavaScript继承设置来建立两个类Item
和Group
。当每个Item
被实例化时,它会引用它的父Group
。 我的问题在我想要建立我的活动时出现,并且通过以下功能最容易解释:
var Group = Class.extend({
...
// Calculate where to place the new item within the group
calculate_new_position: function(item) {
var pos = -1;
// Loop through all DOM items in groups body
$(".item", this.elBody).each(function(i) {
// Retrieve it's class object
var next = $(this).data("_obj");
// Calculating things using the class reference
var lowerPrio = item.tData.priority < next.tData.priority,
lowerId = item.id < next.id;
if(lowerPrio || lowerId) {
pos = i;
return false;
}
});
return pos;
},
...
)};
请注意在上述代码段中使用.data("_obj")
。基本上,当我需要对项目进行排序时,我需要知道对应于组中每个项目的DOM(视图/控制器)的对象(模型)。现在,我可以建立我的Group
课程,这样当我创建每个Item
时,我会在Group
内添加对它的引用(例如Group.items = [i1, i2...]
),然后而不是遍历DOM元素,我可以迭代Item
个实例。但是我想我会遇到类似的问题,比如当我想将Item
移到另一个Group
时(因为Group
不知道Item
1}})。
长话短说:有一个类在实例化时创建一个DOM元素,然后又指向该类,这本质上是危险/天真/无意义的吗?这感觉就像循环依赖,以及一些递归的噩梦,但也许对一个对象的引用并不是一件可怕的事情。如果我正在做其他事情真的愚蠢,并且有一个更简单的方法,那么请指出它。
答案 0 :(得分:2)
任何现代浏览器垃圾收集器都可以处理循环引用。如果您丢失对该对象的所有引用并从DOM中删除所有HTML节点,则您的对象将被垃圾收集(而在DOM中,浏览器将看到对您的对象的引用并防止它被垃圾回收)。但是要注意事件处理程序,如果你不删除事件处理程序,它们也可以保留。
我听说IE的某些旧版本确实存在循环引用的问题。有关depht解释的更多信息: Precise explanation of JavaScript <-> DOM circular reference issue
从那个答案: jQuery的.data()使事情变得更可靠,因为IE的旧版本对于添加到DOM元素的属性存在特殊问题,并且没有正确处理涉及这些属性中的数据的循环引用,因此在它应该不会释放时有(泄漏)。 .data()通过在DOM元素上使用一个添加的属性来解决这个问题,该元素是一个安全,无泄漏的字符串。该字符串是javascript对象的一个键,它可以包含您想要与DOM元素关联的所有属性。因为这些属性都存储在普通的javascript中,浏览器没有循环引用错误,所以这样做不会导致泄漏。
您可能想要查看D3.js,它会执行类似于您想要的操作。它将数据绑定到DOM元素,并提供生成可视化的简便方法: http://d3js.org/
使用D3,您可以将一个数字数组绑定到一个圆形SVG标记数组中,并使其成为每个圆的半径基于与其关联的数组的数量。
编辑:我忘了提到如果你使用$ .remove(),那么事件处理程序也会被删除。但是,如果你有一个闭包内的事件处理程序,它也有一个对你的(删除的)HTML节点的引用,它就不会被垃圾回收。这通常不是一个大问题,因为一旦它在DOM之外它就不会消耗太多资源而且就我所知而言,不可能递归地堆叠那些闭包引用。
答案 1 :(得分:1)
使用jQuery.data
时,它不会对dom元素执行任何操作。 dom元素只是将原始密钥条目存储到jQuery内部缓存中,而不管*。因此,您创建的引用是从JS到JS:
var div = document.createElement("div");
$(div).data("asd", "daa")
console.log(div[jQuery.expando]);
//41
console.log(jQuery.cache[41].data.asd);
//"daa"
(显然上面是jQuery内部,不应该依赖于生产代码)
也就是说,如果你没有通过jQuery进行所有的操作,那么数据就会泄漏到那里,因为如果你背后jQuery就不会得到通知。
使用窗口小部件类的最佳做法是提供一个destroy
方法,删除它负责的所有元素,取消绑定它附加的任何事件,并将其所有DOM引用设置为null。
*如果绝对没有关于元素的事件或数据,则表示不存在条目。但是如果条目因任何原因而存在,那么使用jQuery.data将不会与元素本身交互。
答案 2 :(得分:1)
@ Hoffmann的回答很好,因为他提供了实用的建议和解释。我提供了一个不同的视角,受到Backbone.js如何工作的启发。这是对@vsr在初始评论中建议的扩展。
基本要点:根本不要将JS对象存储在$ .data()中。
使用$ .data()建议您使用$(foo)查找DOM对象,然后深入查看其属性以获取自定义JS函数,这些函数可能在更多DOM元素上运行以获取 自定义功能。这太复杂而且效率低下。
更好的方法是创建一组“纯JS”对象,使用适合您需要的任何语法和机制。然后,这些对象可以具有指向其DOM元素的属性,以用于需要它的少数情况。 Backbone(再次)建议也可以缓存jQuery选择的DOM元素。处理这些“纯JS”对象比通过$ .data()更快更容易维护。它还增加了JS和HTML之间的抽象,这是一件好事。
此外,我发现一种好的工作方式是不要嵌套你的对象,即使只有每一对一的关系。不要制作“组”的“项目”属性。相反,维护组和项的数组,并让一个(或两个)通过引用而不是成员资格来管理关联。