【问题标题】:How is cyclic referencing handled in javascript and browsers?在 javascript 和浏览器中如何处理循环引用?
【发布时间】:2012-06-23 00:11:27
【问题描述】:

我一直在探索各种 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 之类的东西)。

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

【问题讨论】:

  • 这不是真正的“递归”。问题是循环引用。
  • 更改标题以反映这一点

标签: javascript recursion buster.js


【解决方案1】:

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

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

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

【讨论】:

  • 我没有使用这样的工具 - buster.js 是一个测试框架。它做断言。 spy 只是一个被调用的函数。它像 jstestdriver 一样工作并捕获设备并将它们用作主机来运行测试。 busterjs.org
  • 这就是我说的工具。
  • 那么断言工具如何查找引用?它所做的只是,spy = this.spy(); spy(); assert.called(spy); - 它不会以任何方式触及代码或需要了解它 - 它所关心的是它正在运行......
  • 循环引用没有比普通引用更大的内存占用。每个垃圾收集器都会收集它们。 spy() 函数来自sinon.js,而不是来自 buster.js 的人。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-27
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多