为什么这会递归并导致无限循环?

时间:2014-01-14 17:40:43

标签: javascript backbone.js recursion

我正在扩展Backbone.View以支持子视图,我有一个addSubview方法,只是将一个Backbone视图添加到哈希。很容易。我现在正试图这样做,只要你destroy一个视图它就会破坏所有的子视图。我认为这也很容易,但我的方法是无限循环:

destroy: function () {
  debugger;
  // Call destroy on all subviews. If the subviews have subviews they'll
  // be destroyed as well
  for (var v in this._subviews) {
    this._subviews[v].destroy();
  }

  // Instead of calling `delete` on every view we wipe everything out after
  // we're done destroying all the views
  this._subviews = {};

  // Finally, since all the subviews are destroyed it's safe to destroy
  // this view
  this.remove();
},

发生的情况是,调试器是第一次调用而this是调用destroy调用(右),第二次调用第一次子视图(右),第三次调用 - ∞它仍然保持调用第一个子视图。它一步一步地玩:

  1. 点击调试器
  2. 转到this._subviews[v].destroy();行和v ==第一个子视图
  3. 跳回顶层调试器。
  4. 这就是全部。它会永远重复。任何想法或建议?

    演示:http://jsbin.com/iyApuga/1/edit

2 个答案:

答案 0 :(得分:3)

这里发生的事情是object调用_subviews存储在Foo.View的原型上(Backbone的extend方法正在执行此操作)并因此被共享通过 Foo.View的所有实例。这是设置的地方:

Foo.View = Backbone.View.extend({
    _subviews: {},  // right here

这会导致问题,因为当您添加第一个子视图时,会将其添加到this._subviews。由于_subviews对象由View的每个实例共享,因此当您将子视图添加到子视图时,所有其他视图都认为View是他们自己的子视图。

具体来说,这里发生的是当您致电.destroy()时,在您的循环顶部调用所有子视图上的destroy()会意外地再次在同一个视图上调用destroy()。在循环之后的几行之前,_subviews变量不会被清除:

// Instead of calling `delete` on every view we wipe everything out after
// we're done destroying all the views
this._subviews = {};

因此,它变得无限,因为它一遍又一遍地遍历同一个_subviews列表,卡在第一个似乎永远不会被删除的地方。

为了让世界满意,_subviews对象应该是每个View的实例变量,而不是View原型。为此,您应该为每个新View分配一个新的:

  initialize: function () {
    this._subviews = {};
  }

答案 1 :(得分:2)

问题是_subviews在父Foo Foo实例之间被共享。修复方法是为每个实例初始化_subviews

initialize: function () {
    this._subviews = {};
}

尝试以面向类的风格使用Javascript时,这是一个非常常见的问题。