为什么JQuery没有公开其UUIDfunction?

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

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

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

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

$.getUuid(tag) :检查并创建一个UUID(如果不存在) – 理想情况下,该方法是从$.data()分解出来的,并为传入的标记创建或获取uuid。

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

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

我认为这里显而易见的答案是,jQuery为内部使用构建了他们的uuid,并没有看到一个很大的理由或很大的需求来打扰它公开消费。 这并不意味着原因不存在,只是因为它们似乎没有足够的重要性使它成为工作列表的首要位置。

单调增加计数器用作唯一ID是非常简单的实现,我已经多次使用过。 我不觉得我需要类库支持才能这样做。

我认为你对内存泄漏的恐惧,因为你保持对象参考周围有点夸大其词。 首先,如果你摆脱了对象而忘记摆脱对它的一些引用,它只是一个内存泄漏。 这只是垃圾收集语言中的一般规则,你必须“知道”你要保留对象的引用,当你打算释放对象时,你可以清除这些对象并清理这些引用。

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

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

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

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

这被提交给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周期但足够干净。