我想要一个 Dictionary<Node, Object>
这基本上是一个ES6 WeakMap但我需要使用 IE8。
我想要的主要功能是
- 最大限度地减少内存泄漏
- 在给定节点上查找对象的时间复杂度为 O(1)。
我的实现:
var uuid = 0,
domShimString = "__domShim__";
var dataManager = {
_stores: {},
getStore: function _getStore(el) {
var id = el[domShimString];
if (id === undefined) {
return this._createStore(el);
}
return this._stores[domShimString + id];
},
_createStore: function _createStore(el) {
var store = {};
this._stores[domShimString + uuid] = store;
el[domShimString] = uuid;
uuid++;
return store;
}
};
我的实现时间复杂度为 O(1),但存在内存泄漏。
实现这一点以最大程度地减少内存泄漏的正确方法是什么?
最佳答案
在我最近写的一篇文章中,ES 6 - A quick look at weak maps ,我解释了 jQuery 如何能够制作 data()
无泄漏。它基本上生成一个扩展属性名称, jQuery.expando
。当您将数据附加到元素时,数据将被推送到内部缓存数组,并且该元素将被赋予 Expando 属性以及缓存中数据的索引值。与此类似的东西:
element[jQuery.expando] = elementId;
防止循环引用的方法是不要将对象作为扩展直接附加到元素上。如果对元素的引用保留在代码中,则即使从 DOM 中删除该元素,也无法对该元素进行垃圾回收。然而,防止循环引用并不能完全堵住泄漏——如果从 DOM 中删除元素并进行垃圾收集,数组中仍然残留有数据。因此,jQuery 会在页面卸载时清除数组,如果使用其自己的方法(如 remove()
)从 DOM 中删除元素,也会从数组中删除数据。 。它使数据保持事件状态 detach()
.
jQuery 这样做的原因是因为没有等效的弱映射,它在 ES 5 中是可调整的,但在 ES 3 中则不然。正如我的文章 WeakMap
中所解释的那样。正是针对这种情况而设计的,但唯一可用的实现是在 Firefox 6 及更高版本中,并且由于规范尚未最终确定,因此也不应该在生产环境中使用。
从我的文章中可以了解到的另一件事是,某些元素不允许您附加扩展属性 - <object>
和<embed>
是 jQuery 源代码中点名羞辱的两个罪魁祸首。对于这些元素,你几乎被搞砸了,jQuery 只是不允许你使用 data
在他们。
当两个对象的属性相互持有直接引用时,在引用计数实现中会发生基本的循环引用内存泄漏。因此 DOMObject 持有对 JSObject 的引用,反之亦然。假设没有其他对象对这两个对象进行引用,则它们的永久引用计数都为 1,并且 GC 不会将它们标记为要收集。
旧版浏览器 (IE6) 不会破坏这些循环引用,即使在页面卸载时也是如此,而较新的浏览器能够通过识别导致循环引用的模式来破坏其中的许多循环引用。 jQuery.cache
类似的模式会部分避免内存泄漏,因为 DOMObject 永远不会持有对 JSObject 的引用,因此,即使 JSObject 持有对 DOMObject< 的引用/em>,当没有更多引用时,GC 仍然可以将 JSObject 标记为回收。一旦 GC 收集了 JSObject,DOMObject 的引用计数就会减少,从而将其释放以供收集。
尽管 IE 8+ 和其他引用计数浏览器可能能够打破许多循环引用模式(IE 8 修复了大约 400 个循环引用模式),但泄漏的可能性只会降低。例如,在使用脚本元素和 JSONP 时,我在 IE 8 中的自己的应用程序之一中发现了巨大的泄漏。最好的解决方案是做最坏的打算,而不WeakMap()
,您能做的最好的事情就是使用 jQuery 数据模式。当然,您可能会冒拥有孤立对象的风险,但这是两害相权取其轻。
关于javascript - 用于 DOM 节点引用的 IE8 兼容弱映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8346738/