6

我正在尝试采用现有的工作代码库并使用 JavaScript 使其面向对象。我的系统采用一对多关系中包含项目的JSON ,并在页面上将其可视化。这些项目可以移动到不同的组中,并且它们在这些组中的位置也需要计算。因此,需要建立了解周围团体和门票的活动。

我正在使用John Resig 的简单 JavaScript 继承设置来建立两个类,Item并且Group. 当 eachItem被实例化时,它会引用它的 parent 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类,以便当我创建每个类时,我从我的(例如 so )Item中添加对它的引用,然后我可以迭代实例而不是迭代 DOM 元素。但是,我认为我会遇到类似的问题,例如当我想将 an 移动到另一个时(因为不会知道)。GroupGroup.items = [i1, i2...]ItemItemGroupGroupItem

长话短说:拥有一个在实例化时创建一个 DOM 元素然后又指向该类的类是否本质上是危险/幼稚/无意义的?这感觉像是循环依赖和一些递归的噩梦,但也许对对象的引用并不是一件可怕的事情。如果我正在做其他非常愚蠢的事情,并且有一个更简单的方法,那么也请指出这一点。

4

3 回答 3

2

任何现代浏览器垃圾收集器都可以处理循环引用。如果您丢失对对象的所有引用并从 DOM 中删除所有 HTML 节点,您的对象将被垃圾回收(而它在 DOM 中,浏览器将看到对您的对象的引用并防止它被垃圾回收)。不过要小心事件处理程序,如果您不删除它们,它们也可以保留引用。

我听说一些旧版本的 IE 确实存在循环引用的问题。有关详细说明: JavaScript <-> DOM 循环引用问题的精确解释

从那个答案:jQuery的 .data() 使事情更可靠,因为旧版本的 IE 在添加到 DOM 元素的属性方面存在特殊问题,并且没有正确处理涉及这些属性中数据的循环引用,因此不会释放应该有的东西(泄漏)。.data() 通过仅在 DOM 元素上使用一个添加的属性来解决此问题,该属性是安全的、不泄漏的字符串。然后,该字符串是一个 javascript 对象的键,该对象可以包含您希望与 DOM 元素关联的所有属性。因为这些属性都存储在纯javascript中,浏览器没有循环引用错误,这样做不会导致泄漏。

您可能想要查看 D3.js,它的功能与您想要的类似。它将数据绑定到 DOM 元素并提供生成可视化的简单方法:http: //d3js.org/

使用 D3,您可以将一个数字数组绑定到一个圆形 SVG 标记数组,并使其具有基于与其关联的数组的编号的每个圆的半径。

编辑:我忘了提到如果你使用 $.remove() 事件处理程序也会被删除。但是,例如,如果您有一个位于闭包内的事件处理程序,该处理程序还引用了您的(已删除)HTML 节点,则它不会被垃圾收集。这通常不是一个大问题,因为一旦它在 DOM 之外,它就不会消耗太多资源,而且据我所知,不可能递归地堆叠这些闭包引用。

于 2013-08-09T12:15:44.137 回答
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 将不会与元素本身交互。

于 2013-08-13T16:15:44.497 回答
1

@Hoffmann 的回答很好,因为他提供了实用的建议和解释。我提供了不同的视角,灵感来自 Backbone.js 的工作原理。这是@vsr 在他们最初的评论中建议的扩展。

基本要点:根本不要将 JS 对象存储在 $.data() 中。

使用 $.data() 建议您使用 $(foo) 查找 DOM 对象,然后深入了解它们的属性以获取您的自定义 JS 函数,这可能会在更多 DOM 元素上运行以获取它们的自定义函数。这是过于复杂和低效的。

更好的方法是创建一组“纯 JS”对象,使用适合您需要的任何语法和机制。然后,这些对象可以有一个属性指向它们的 DOM 元素,以便在少数需要它的情况下使用。Backbone(再次)建议同时缓存 jQuery 选择的 DOM 元素是一种很好的做法。处理这些“纯 JS”对象将比通过 $.data() 更快、更容易维护。它还增加了 JS 和 HTML 之间的抽象,这是一件好事。

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

于 2013-08-15T17:08:07.227 回答