我使用knockout.js构建SPA。在我的视图模型中,我经常使用"在飞行中"计算跟踪变化和维持状态的技巧。这是一个例子:
var fileBrowserViewModel = function() {
this.currentFolder = ko.observable("/");
this.folderPath = ko.observableArray(["/"]);
// on-the-fly created computed to maintain folderpath when currentFolder changes
ko.computed(function() {
var index = this.folderPath.indexOf(this.currentFolder());
if (index < 0)
this.folderPath.push(this.currentFolder());
else
this.folderPath.splice(index+1, this.folderPath().length-index);
}, this);
}
我有一个逻辑,它处理viewmodel的每个公开公开的计算属性。但是,在发布包装视图模型后,这些隐式声明的计算结果会发生什么? 处理计算的方式和时间?它会导致内存泄漏吗?
答案 0 :(得分:4)
如何以及何时处理完毕的计算?
从某种意义上说,他们的.dispose
函数从未被调用过。
会导致内存泄漏吗?
也许,但并非总是如此。计算需要.dispose
'd的原因是因为他们对依赖项进行了订阅;这意味着每个依赖项都持有对计算的引用。当不再有任何引用时,计算将收集垃圾。在上面的示例中,当ViewModel本身不再在任何地方引用时会发生这种情况,因此计算的内容将按照预期与其ViewModel一起收集垃圾。
但是,如果计算取决于包含它的ViewModel之外的任何内容,则它将不会被垃圾收集,直到它所依赖的所有内容本身都准备好被垃圾收集。在最坏的情况下,计算依赖于从不垃圾收集的全局,计算机本身永远不会被垃圾收集。
因此,真正的答案是,只有需要才能处理计算机,因为它具有比计算本身更长寿命的依赖项。但是,由于处理一个无论如何都会收集垃圾的计算机没有什么害处,因此通常更容易确保ViewModel创建的所有计算机都被处理掉了;而不是试图分开哪些需要处理。
如何处理计算处置
在任何情况下,您都希望在viewModel或某些等效方法上使用.dispose
方法。一些外部逻辑无法知道私有创建的计算机,因此ViewModel本身需要自己处理它。处理此问题的简单方法是在VM的.dispose
方法中为每个私有创建的计算机调用.dispose
:
var fileBrowserViewModel = function() {
// on-the-fly created computed to maintain folderpath when currentFolder changes
var folderPathComputed = ko.computed(function() {
//implementation omitted
}, this);
this.dispose = function () {
folderPathComputed.dispose();
}
}
如果你想减少手动;您可以创建一个实用程序来处理您的处理,例如:
function ComputedManager() {
var computedsToDispose = [];
this.computed = function() {
//create a computed normally, with the provided arguments
var computed = ko.computed.apply(ko, arguments);
computedsToDispose.push(computed);
return computed;
}
this.dispose = function() {
computedsToDispose.forEach(function(computed) {
computed.dispose();
});
}
}
var fileBrowserViewModel = function() {
var computedManager = new ComputedManager();
// on-the-fly created computed to maintain folderpath when currentFolder changes
computedManager.computed(function() {
//implementation omitted
}, this);
this.dispose = computedManager.dispose;
}
总是调用computedManager.computed
而不是ko.computed
有点尴尬,但它比手动清理每个计算器更容易,并且好处是你可以用这种方式创建所有计算,然后消除了对其他逻辑的需要,以及查找和处理所有公开暴露的计算。