如何在javascript和浏览器中处理循环引用?

时间:2012-06-20 19:19:04

标签: javascript recursion buster.js

我一直在探索各种MV *框架中的模式,今天注意到一个奇怪的模式,这似乎会导致一些问题

模型原型。有一个属性collections: []

收集原型。有一个属性models: []

当一个集合获得一个新模型时,它被推入collection.models但模型本身也被装饰以了解它所属的集合 - 即集合实例被推入{{1 }}

所以model.collections是一个集合,其中包含model.collections[0]作为具有集合属性的模型......等等。

最基本的:

.models[0]

以下是行动中的有罪方:https://github.com/lyonbros/composer.js/blob/master/composer.js#L310-313然后再向下推进模型:https://github.com/lyonbros/composer.js/blob/master/composer.js#L781-784

我认为会有一定程度的懒惰评估 - 在需要之前不会使用任何东西。该代码 - 单独 - 有效。

但我也是通过buster.js编写测试的,我发现所有依赖var A = function() { this.collections = []; }, B = function() { this.models = []; this.add = function(what) { what.collections.push(this); this.models.push(what) }; }; var model = new A(); var collection = new B(); collection.add(model); 的测试都产生sinon.spy()(FF)或InternalError: too much recursion(Chrome)。捕获的FF甚至无意中崩溃,我以前从未遇到过破坏者测试驱动程序 - 它甚至在午休时使用了3.5gb的ram。

经过相当多的调试后,我解除了参考存储空间,突然间,它再次正常工作。不可否认,删除RangeError: Maximum call stack size exceeded断言也有效,但这不是重点。

所以,问题是 - 拥有这样的代码,是否可以接受,浏览器将如何解释它,瓶颈是什么以及如何使用指向其所属集合的指针来装饰模型(可能是集合控制器)和收集uids或其他东西)。

将失败的buster.js测试的全部要点:https://gist.github.com/2960549

2 个答案:

答案 0 :(得分:2)

浏览器不关心。问题是您使用的工具无法通过对象图检查循环引用链。这些都是完全合法的,至少它们是如果你想要它们并期望它们。

如果您考虑一个对象及其属性,以及通过这些属性直接或间接引用的对象,那么该程序集构成一个图形。如果可以跟随参考文献并回到您开始的地方,那么这意味着该图表具有循环。语言允许循环肯定是一件好事。在给定系统中是否合适取决于相关代码。

因此,例如,如果图形是循环的,那么遍历对象图而不检查它是否已经访问过对象的递归函数肯定会触发“过多的递归”错误。

答案 1 :(得分:1)

只有两个对象相互引用(称为“循环引用”)。

var a, b = {a: a={b: b}};
// a.b: pointer to b
// b.a: pointer to a

根本没有递归。如果您收到too much recursionMaximum call stack size exceeded错误,则需要有一个过于频繁调用的函数。这可以是例如当您尝试克隆对象并递归属性而不关心循环引用时会发生这种情况。您需要进一步查看代码,错误消息应包括(非常长的)调用堆栈。