当您将对 DOM 对象的引用作为该 DOM 对象的属性放在 DOM 对象上时,某些浏览器中会出现循环引用问题。然后,您就有两个互相指向的 DOM 对象。删除带有自定义属性的 DOM 对象并不会清除该自定义属性。不太聪明的垃圾收集器不会意识到这个 DOM 引用不算数,因此它会被卡住,并且有多种方式可能导致泄漏。
.data()
解决了这个问题,因为.data()
数据不在 DOM 对象上。它只是一个 JavaScript 数据结构,可以通过唯一的字符串 ID 与 DOM 对象关联。
其中一个令人困惑的部分是,当你阅读时.data("key")
和key
在 JavaScript 中找不到.data()
数据结构,然后,只有那时,jQuery 才会在 DOM 对象上查找名为"data-key"
。但每当你用.data("key", "myData")
,它从不写入 DOM 对象,只写入 javascript 数据结构。
因此,自从.data()
从不将数据写入 DOM 对象,因此不会出现某些浏览器遇到问题的任何此类循环引用。
还有一些其他有用的事情需要了解.data()
数据结构。当你使用 jQuery 时.remove()
从 DOM 中删除元素或者当你调用$(elem).html("new html")
, jQuery 清除.data()
有关任何已删除项目的数据。在这种情况下,最好不要将 jQuery 与纯 JavaScript 混合在一起。如果您正在使用.data()
,那么您应该始终使用 jQuery 函数从 DOM 中删除项目,以便.data()
被适当地清理。否则,您可能会以这种方式出现内存泄漏(.data()
数据可能会泄漏,并且任何已删除的 DOM 对象在.data()
可能会泄漏。但是,如果您只使用 jQuery 方法从 DOM 中删除项目(包括替换innerHTML),那么 jQuery 会适当地清理内容,并且不会出现泄漏。
因此,例如,这将造成内存泄漏:
// suppose elem is a DOM element reference
// store some data in jQuery's data storage on behalf of a DOM element
$(elem).data("someKey", "someValue");
// remove DOM element with plain Javascript
elem.parentNode.removeChild(elem);
因为您使用纯 Javascript 删除了 DOM 元素,所以 jQuery 没有机会清理您之前存储的数据。 DOM 元素本身将被垃圾回收,但是.data()
您之前存储的值现在在 jQuery 的存储中是孤立的,本质上是“泄漏”,因为它可能永远不会被清除。另一方面,如果你这样做:
$(elem).data("someKey", "someValue");
$(elem).remove();
然后,jQuery 将看到您正在删除 DOM 元素,并且还将清除您存储的数据.data()
.
了解其工作原理的一个相当简单的方法是使用非最小化版本的 jQuery 创建几行脚本,然后单步调用$(elem).data("key", "whatever")
在调试器中观察它是如何工作的。