符合IE8标准的DOM节点引用的弱映射

时间:2011-12-01 18:56:10

标签: javascript internet-explorer-8 weakmap

我希望字面意思是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)但是有内存泄漏。

实现此目的的最佳方法是什么,以尽量减少内存泄漏?

1 个答案:

答案 0 :(得分:4)

在我最近写的一篇文章ES 6 - A quick look at weak maps中,我解释了jQuery如何使data()无泄漏。它基本上生成一个expando属性名称jQuery.expando。将数据附加到元素时,数据将被推送到内部缓存数组,并为该元素提供expando属性,其值为缓存中数据的索引。类似的东西:

element[jQuery.expando] = elementId;

防止循环引用的方法是不将对象作为expandos直接附加到元素。如果对元素的引用仍保留在代码中,那么即使将该元素从DOM中删除,也不能对该元素进行垃圾回收。但是,阻止循环引用不会完全堵塞泄漏 - 如果从DOM中删除元素并收集垃圾,则数组中仍会保留数据。因此,如果使用自己的方法(例如remove())从DOM中删除元素,jQuery将在页面卸载时清除数组,以及从数组中删除数据。它使detach()的数据保持活跃状态​​。

jQuery执行此操作的原因是因为没有弱映射等效,它在ES 5中是可调的,但在ES 3中没有。正如我的文章中所解释的那样,WeakMap就是针对这种情况而制作的,但唯一可用的实现是在Firefox 6及更高版本中,并且规范尚未最终确定,即使不应在生产环境中使用。

从我的文章中得到的另一件事是某些元素不允许你附加expando属性 - <object><embed>是jQuery源代码中命名和羞辱的两个元凶。对于这些元素,你几乎搞砸了,jQuery不允许你对它们使用data


当两个对象的属性彼此直接引用时,在引用计数实现中会发生基本循环引用内存泄漏。所以 DOMObject 包含对 JSObject 的引用,反之亦然。假设没有对这两个对象的其他引用,它们都具有永久引用计数1并且GC不会将它们标记为收集。

较旧的浏览器(IE6)不会破坏这些循环引用,即使在页面卸载时也是如此,而较新的浏览器能够通过识别导致它们的模式来破坏许多这些循环引用。 jQuery.cache和类似的模式部分地使内存泄漏无效,因为 DOMObject 永远不会持有对 JSObject 的引用,所以,即使 JSObject 拥有对 DOMObject ,当没有更多的引用时,GC仍然可以将 JSObject 标记为集合。一旦GC收集了 JSObject DOMObject 的引用计数就会减少,也可以将其释放出来进行收集。

虽然IE 8+和其他引用计数浏览器可能能够打破许多循环引用模式(IE 8修复了大约400个),但泄漏的可能性只会降低。例如,在使用脚本元素和JSONP时,我在IE 8中看到了我自己的一个应用程序中的大量泄漏。最好的解决方案是计划最坏的情况,如果没有WeakMap(),你可以做的最好就是使用jQuery数据模式。当然,你可能冒着孤儿的危险,但这是两个邪恶中较小的一个。