JavaScript类和jQuery对象之间的循环依赖

时间:2013-08-09 10:52:19

标签: javascript jquery oop circular-dependency

我正在尝试使用现有的工作代码库,并使用JavaScript使其面向对象。我的系统在一对多关系中使用包含 groups items 的JSON,并在页面上显示此内容。这些项目可以移动到不同的组中,并且还需要计算它们在这些组中的位置。因此,需要建立能够了解周围群组和门票的事件。

我正在使用John Resig's简单的JavaScript继承设置来建立两个类ItemGroup。当每个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元素,然后又指向该类,这本质上是危险/天真/无意义的吗?这感觉就像循环依赖,以及一些递归的噩梦,但也许对一个对象的引用并不是一件可怕的事情。如果我正在做其他事情真的愚蠢,并且有一个更简单的方法,那么请指出它。

3 个答案:

答案 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之间的抽象,这是一件好事。

此外,我发现一种好的工作方式是不要嵌套你的对象,即使只有每一对一的关系。不要制作“组”的“项目”属性。相反,维护组和项的数组,并让一个(或两个)通过引用而不是成员资格来管理关联。