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

How is cyclic referencing handled in javascript and browsers?

本文关键字:处理 循环 引用 浏览器 JavaScript      更新时间:2023-09-26

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

模型原型。具有属性collections: []

集合原型。具有属性models: []

当集合获得新模型时,它被推送到collection.models但模型本身也被修饰为知道它是其成员的集合 - 即集合实例被推送到model.collections

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

最基本的:

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);

这是有罪的一方在行动: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我注意到所有依赖sinon.spy()的测试都在产生InternalError: too much recursion(FF)或RangeError: Maximum call stack size exceeded(Chrome)。捕获的 FF 甚至无响应地崩溃,这是我以前从未遇到过 buster 测试驱动程序 - 它甚至在我午休时间使用了 3.5gb 的内存。

经过相当多的调试后,我撤消了引用存储,突然间,一切又恢复正常了。诚然,删除spy()断言也有效,但这不是重点。

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

将失败的破坏者.js测试的完整要点:https://gist.github.com/2960549

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

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

因此,例如,一个递归函数遍历对象图

而不检查它是否已经访问过某个对象,如果图是循环的,肯定会触发"太多递归"错误。

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

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

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