2

我想从字面上看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) 但有内存泄漏。

实现此以最大程度地减少内存泄漏的正确方法是什么?

4

1 回答 1

4

在我最近写的一篇文章ES 6 - A quick look atweak maps中,我解释了 jQuery 如何实现data()无泄漏。它基本上会生成一个 expando 属性名称,jQuery.expando. 当您将数据附加到一个元素时,数据被推送到一个内部缓存数组,并且该元素被赋予了具有缓存中数据索引值的 expando 属性。与此类似的东西:

element[jQuery.expando] = elementId;

防止循环引用的方法是不要将对象作为扩展直接附加到元素上。如果对该元素的引用保留在代码中,则即使该元素已从 DOM 中删除,也无法对该元素进行垃圾回收。但是,防止循环引用并不能完全堵塞泄漏——如果从 DOM 中删除元素并收集垃圾,数组中仍然会留下数据。因此,jQuery 在页面卸载时清除数组,如果元素从 DOM 中删除,则使用它自己的方法(如remove(). 它使数据保持活动状态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 收集了JSObjectDOMObject的引用计数将减少,也将其释放以供收集。

尽管 IE 8+ 和其他引用计数浏览器可能能够破坏许多循环引用模式(IE 8 修复了大约 400 个),但泄漏的可能性只会降低。例如,在使用脚本元素和 JSONP 时,我在 IE 8 中的自己的一个应用程序中看到了巨大的泄漏。最好的解决方案是做最坏的打算,如果没有WeakMap(),你能做的最好的就是使用 jQuery 数据模式。当然,您可能会冒着拥有孤儿对象的风险,但这是两害相权取其轻。

于 2011-12-01T19:08:59.377 回答