6

在底层,JQuery 使用“UUID”的映射(只是它维护的一个计数器jQuery.uuid)来解决浏览器在从 Javascript 将属性附加到 DOM 中的标记时出现的众所周知的内存泄漏问题。代替这样做,JQuery 使用$.data(tag, name, value)将数据存储在与 uuid 键控的映射中(可以通过检查来确定的键tag[jQuery.expando])。

虽然$.data()非常有用,但有时您希望将数据映射到标签而不将该数据转储到一个全局存储桶中 - 您需要自己的较小数据存储桶,例如,检查长度或循环。

作为一个人为的示例,假设您的图标在单击时会在 4 种状态之一中旋转。当一个处于状态 2 时,您希望将其添加到状态 2 的图标数组中。最明显的方法是将标签添加到数组中;但是这样做会造成内存泄漏。您可以调用$.data()复选框,但这并不能完全完成您想要做的事情 - 您必须遍历所有复选框,检查$.data()它们以确定哪些在列表中,哪些不在列表中。

您需要将标签的一些抽象存储在一个数组中,这就是 jQuery 的 UUID。您可以编写自己的 UUID 功能,但理想情况下,出于代码大小和质量原因,您只需利用 JQuery 已经内置的 UUID 功能。您可以要求 JQuery 通过调用隐式地将 UUID 附加到标签上$.data(tag, 'irrelevant', 1),然后检查tag[jQuery.expando]以获取其 UUID,最后在列表中使用它......但这有点小技巧。真正理想的是在公共 API 中公开以下内容:

$.getUuid(tag): 如果不存在 UUID,则检查并创建一个 UUID - 理想情况下,该方法被分解$.data()并创建或获取传入标签的 uuid。

那么,这是否有原因在 jQuery 中没有被考虑到它自己的方法中?这在某种程度上有害吗?它只是从来没有看起来有用的东西吗?

我应该注意,我实际上已经在我们使用的 jQuery 版本中考虑了它,它非常有帮助。但也许我在使用中没有遇到潜在的风险。我也知道有一个插件可以实现这一点,但它有点坏了——并且有 2 个代码路径来执行相同的 UUID 功能既有点浪费又有点脆弱。

4

2 回答 2

4

我认为这里显而易见的答案是 jQuery 构建了他们的 uuid 供内部使用,并且没有看到一个很好的理由或很大的需求来打扰它公开消费。这并不意味着原因不存在,只是它们似乎还不够重要,无法将其排在要处理的事情列表的首位。

用作唯一 ID 的单调递增计数器实现起来非常简单,我已经使用了很多次。我不觉得我需要类库支持来做到这一点。

我认为您担心内存泄漏是因为您保留了对象引用,这有点夸大了。首先,如果您摆脱对象并忘记摆脱对它的某些引用,这只是内存泄漏。这只是垃圾收集语言中的一般规则,您必须“知道”在哪里保留对您可能摆脱的对象的引用,并在您打算释放对象时清理这些引用。

其次,如果您在每页中多次执行相同的操作,或者对象非常非常大,那么这只是有意义的内存泄漏。当你进入下一页时,它们都会被清理掉,所以它不像是永远积累的东西,除非你从不离开那个浏览器页面并且一遍又一遍地做同样的事情,这涉及到删除的对象,但没有删除的引用。

第三,jQuery 的 .data() 机制尝试在使用 DOM 对象时为您解决很多问题。

第四,在您设计的示例中,这不会造成内存泄漏,除非您在状态 2 不再有效或不再使用时不清理您的图标数组。如果您清理它,那么在该数组中存储直接 DOM 引用就没有问题。如果你不清理数组,那么即使数组本身也是内存泄漏,即使它里面有抽象的 uuid 而不是 DOM 引用。大多数时候,使用抽象引用只是比需要的工作多得多。

同样,即使您让它泄漏,只有当页面具有较长的生命周期并且您反复创建和释放对象但没有以引用随时间累积的方式清除对它们的所有引用并执行此操作时,泄漏才重要足以使它们引起的内存泄漏是有意义的。我一直在 JS 变量中保留对 DOM 对象的引用。我只是小心地确保当我不再需要它们时将它们清空,所以我知道 DOM 对象可以在以后的某个时候被释放。

于 2011-08-11T23:06:26.680 回答
0

这被提交给 jQuery 团队并被拒绝。

但是,您可以维护一个将垃圾收集委托给 jQuery 的标签列表,如下所示:

http://jsfiddle.net/b9chris/Un2mH/

基本代码是:

sets[oldIndex] = sets[oldIndex].not(this);
sets[index] = sets[index].add(this);

尽管这会产生单独的内存负担 - 这些方法不仅仅是向数组添加标签,它们还维护此集合的先前状态堆栈(内部调用 .pushStack())。如果页面长期存在大量用户操作,则集合将无限增长。为了防止这种情况,您可以破解对象以删除堆栈:

sets[oldIndex] = sets[oldIndex].not(this);
sets[oldIndex].prevObject = null;
sets[index] = sets[index].add(this);
sets[index].prevObject = null;

浪费了一些 CPU 周期,但足够干净。

于 2013-06-14T00:59:20.233 回答