为什么jQuery没有公开其UUID功能?

6
在JQuery的内部,它使用一个"UUID"映射表(实际上只是一个计数器,由jQuery.uuid维护)来解决当你从Javascript中将属性附加到DOM标签时,浏览器已知的内存泄漏问题。作为替代方案,JQuery使用$.data(tag, name, value)将数据存储在以uuid为键的映射中(可以通过检查tag[jQuery.expando]确定该键)。虽然$.data()非常有用,但有时您希望将数据映射到标签而不是将这些数据倾泻到一个全局桶中——您希望拥有自己的更小的数据桶,例如可以检查长度或循环遍历。举个例子,假设您有图标,当单击时会轮换四种状态之一。当一个图标处于状态2时,您想将其添加到状态2的图标数组中。最明显的方法是将该标签添加到数组中;但是这样做会导致内存泄漏。您可以在复选框上调用$.data(),但这并不能完全实现您要做的事情——您必须循环遍历所有复选框,并根据它们来检查$.data(),以确定哪些在列表中,哪些不在。您需要将标签的某些抽象内容存储在数组中,这就是JQuery的UUIDs所起的作用。您可以编写自己的UUID功能,但理想情况下,为了减小代码大小和提高质量,最好利用JQuery已经内置的UUID功能。您可以通过调用$.data(tag, 'irrelevant', 1)来隐式地请求JQuery附加UUID到标签上,并检查tag[jQuery.expando]来获取其UUID,最后在列表中使用该UUID...但这有点像黑客行为。实际上,最理想的情况是将以下内容公开到公共API中:$.getUuid(tag):检查并创建UUID(如果不存在),理想情况下,该方法应从$.data()中分离出来,并为传入的标签创建或获取uuid。那么,为什么JQuery没有将其分解成自己的方法呢?这会有什么害处吗?还是说从未发现它有用?我应该指出,我实际上已经在我们正在使用的jQuery版本中将其分解出来了,这非常有帮助。但也许我的使用中存在潜在风险。我也知道一个插件可以实现这个功能,但它有点损坏——拥有两个执行相同UUID功能的代码路径既有点浪费,又有点脆弱。

1
我认为这是一个很好的问题,但直接发送电子邮件给jQuery维护者可能更有效 :-) 或者,记录一个bug - 他们确实非常重视bug列表。 - Pointy
2
在将该功能暴露给外界之前,我认为他们必须重新命名它:连续的整数根本就不是普遍唯一的标识符。 - Frédéric Hamidi
@Pointy 好的,我会这样做 - 我非常感激更广泛的Stack Overflow社区和像这样的问题的评论排名系统。 - Chris Moschini
请注意,这个建议曾经被提出给 JQuery 团队,但遗憾地被(相当粗鲁地)拒绝了,基本上是说,我们收到了很多请求,你自己写吧。 - Chris Moschini
2个回答

4
我认为显而易见的答案是,jQuery为内部使用构建了它们的uuid,并没有看到很好的理由或需求去让它公开可用。这并不意味着不存在原因,只是它们似乎还不重要到足以成为工作列表中的重点。
作为唯一ID使用的单调递增计数器非常容易实现,我经常使用它。我觉得我不需要类库支持来做到这一点。
我认为你因为保留对象引用而担心内存泄漏有些过分夸大其词。首先,只有在你丢弃对象并忘记丢弃对它的某些引用时才会出现内存泄漏。这只是垃圾收集语言中的一个通用规则,你必须“知道”你在哪里保留对可能被释放的对象的引用,并在打算释放对象时清理这些引用。
其次,只有在每个页面上重复执行相同操作很多次,或者对象非常大时,才会造成有意义的内存泄漏。当你进入下一页时,它们全部都会被清除,所以它不像是一种永久积累的东西,除非你永远不离开那个浏览器页面并且反复执行涉及已删除对象但未删除引用的相同操作。
第三,当使用DOM对象时,jQuery的.data()机制会尝试为你解决很多这样的问题。
第四,在你虚构的例子中,只有在状态2中的图标数组不再有效或不再使用时,你没有清理它时才会创建内存泄漏。如果你清理了它,那么在该数组中存储直接DOM引用就没有问题。如果你不清理数组,那么即使它包含抽象uuid而不是DOM引用,该数组本身也是一个内存泄漏。大多数情况下,使用抽象引用只会增加更多的工作量。
再次强调,即使你让它泄漏,泄漏只有在页面寿命较长且你反复创建和释放对象,但没有以一种可以累积引用并导致有意义的内存泄漏的方式清除所有引用时才重要。我经常在JS变量中保留对DOM对象的引用。我只是小心确保在不再需要它们时将它们设置为null,这样我知道DOM对象可以在未来某个时候被释放。

我不确定您是否理解浏览器内存泄漏的原因。例如,uuid数组只是数字数组。这个:var array = [1,2,3,4]; 在Javascript中不会泄漏 - 对于浏览器来说,uuid数组也是一样的。现在,如果您不清理标签中添加的uuid,则会泄漏,但这就是利用现有的jQuery代码的好处 - 您可以重用所有处理该添加、跟踪和清理的经过充分测试的代码,而不是自己拼凑。所以,我知道我可以编写自己的代码 - 只是这不是一个好主意。 - Chris Moschini
我绝对理解什么会导致浏览器内存泄漏。我以前在真实的代码中追踪过它们。我不理解的是,您认为需要使用uuid来保护泄漏的内容,而jQuery已经提供了功能。如果我理解您的示例,它不会泄漏。仅将DOM对象引用存储在数组中并不会导致泄漏,就像存储uuid一样。其他情况(例如从DOM中移除DOM对象并释放所有其他引用)会导致泄漏,并且这种情况足够重要。 - jfriend00
正确 - 它基本上会创建一种可能发生泄漏的情况(只需要DOM移除),而我不想编写脆弱的代码。 - Chris Moschini
此外,可能需要删除一个DOM才能造成泄漏。但是,如果没有相应的JS引用清理,可能需要数千个DOM删除才能造成显著的泄漏。这在某些情况下可能会发生,但并不是通常的网页。 - jfriend00

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周期,但还是比较干净的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接