敲除映射插件中的奇怪内存泄漏

时间:2014-12-15 18:16:22

标签: javascript knockout.js knockout-mapping-plugin

无法弄清楚为什么在使用knockout.mapping插件创建视图模型的情况下,处理计算的observables不会从全局变量中删除订阅。
首先让我们看看直接创建模型时会发生什么:

// Global variable.
var Environment = {
    currencyStr: ko.observable("usd.")
};
// Item model, used intensively.
function ItemModel(price) {
    var self = this;
        
    this.price = ko.computed(function () {
        // Computed is subscribed to global variable.
        return price + ' ' + Environment.currencyStr();
    });
};

ItemModel.prototype.dispose = function () {
    // Dispoing subscription to global variable.
    this.price.dispose();
};

function ViewModel() {
    var self = this;
    
    self.items = ko.observableArray([]);
    // Simply adds 1000 new items to observable array directly.
    self.addItems = function () {
        for (var i = 0; i < 1000; i++) {
            self.items.push(new ItemModel(i));
        }
    };
    // Disposes and removes items from observable array
    this.removeItems = function () {
        ko.utils.arrayForEach(self.items(), function (item) {
            item.dispose();
        });
        self.items.removeAll();
    };		
};

ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<button data-bind="click: addItems">Add items</button>
<button data-bind="click: removeItems">Remove items</button>
<div data-bind="foreach: items">
    <div>
        <span data-bind="text: price"></span>
    </div>
</div>

我使用Chrome开发工具记录堆分配,同时多次添加和删除项目。每次添加后,先前分配的对象都已成功清理,我得到以下图片:

enter image description here

现在使用映射插件的功能相同:

// Global variable.
var Environment = {
    currencyStr: ko.observable("usd.")
};
// Item model, used intensively.
function ItemModel(price) {
    var self = this;
        
    this.price = ko.computed(function () {
        // Computed is subscribed to global variable.
        return price + ' ' + Environment.currencyStr();
    });
};

ItemModel.prototype.dispose = function () {
    // Dispoing subscription to global variable.
    this.price.dispose();
};

function ViewModel() {
    var self = this;
    
    self.items = ko.observableArray([]);
    self.itemsMapping = {
        'create': function (options) {
            return new ItemModel(options.data);
        }
    };
    // Simply adds 1000 new items to observable array using mapping plugin.
    self.addItems = function () {
        var itemsPrices = new Array(1000);
        for (var i = 0; i < 1000; i++) {
            itemsPrices[i] = i;
        }
        // Mapping new array to our observable array.
        ko.mapping.fromJS(itemsPrices, self.itemsMapping, self.items);
    };
    // Disposes and removes items from observable array
    this.removeItems = function () {
        ko.utils.arrayForEach(self.items(), function (item) {
            item.dispose();
        });
        self.items.removeAll();
    };		
};

ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<button data-bind="click: addItems">Add items</button>
<button data-bind="click: removeItems">Remove items</button>
<div data-bind="foreach: items">
    <div>
        <span data-bind="text: price"></span>
    </div>
</div>

使用相同的技术记录堆分配,这就是我所看到的:

enter image description here

我知道pureComputed,但我希望避免使用它们有两个原因:

  1. 切换到纯计算通过抛出异常来破坏遗留代码:
      

    ''''pure'计算不能递归调用

  2. 解决这些问题需要花费很多时间。

    1. 更频繁地评估纯计算机,这会产生一些我想避免的性能开销,并且这再次影响遗留代码,这是不可预测的。
    2. 此外,我仍然希望使用映射插件,因为它能够监视集合状态(使用key映射属性),因为它为我创建了所有可观察对象。

      那么我是否遗漏了一些东西,以及在使用映射插件时释放资源的正确方法是什么?

1 个答案:

答案 0 :(得分:5)

深入研究映射插件,它对计算机进行了一些黑客攻击,并且在这种情况下显然会破坏它。

将计算出的价格设置为deferEvaluation似乎会使映射插件在很大程度上不受影响。

this.price = ko.computed(function () {
    // Computed is subscribed to global variable.
    return price + ' ' + Environment.currencyStr();
}, this, { deferEvaluation: true });