【问题标题】:How to get javascript object references or reference count?如何获取 javascript 对象引用或引用计数?
【发布时间】:2011-02-25 14:22:00
【问题描述】:

如何获取对象的引用计数

  • 是否可以确定一个 javascript 对象是否有多个引用
  • 或者如果它有引用除了我正在访问它的那个
  • 或者甚至只是为了获取引用计数本身?
  • 我能否从 javascript 本身中找到这些信息,或者我需要跟踪我自己的引用计数器。

显然,我的代码必须至少有一个对它的引用才能访问该对象。但我想知道是否有其他引用它,或者我的代码是否是唯一可以访问它的地方。如果没有其他对象引用它,我希望能够删除该对象。

如果您知道答案,则无需阅读此问题的其余部分。以下只是一个示例,以使事情更清楚。


用例

在我的应用程序中,我有一个名为 contactsRepository 对象实例,其中包含 ALL 我的联系人数组。还有多个Collection 对象实例,例如friends 集合和coworkers 集合。每个集合都包含一个数组,其中包含一组与 contacts Repository 不同的项。

示例代码

为了使这个概念更具体,请考虑下面的代码。 Repository 对象的每个实例都包含一个特定类型的所有项目的列表。您可能有一个联系人存储库和一个单独的事件存储库。为简单起见,您可以获取、添加和删除项目,并通过构造函数添加许多项目。

var Repository = function(items) {
  this.items = items || [];
}
Repository.prototype.get = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      return this.items[i];
    }
  }
}
Repository.prototype.add = function(item) {
  if (toString.call(item) === "[object Array]") {
    this.items.concat(item);
  }
  else {
    this.items.push(item);
  }
}
Repository.prototype.remove = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      this.removeIndex(i);
    }
  }
}
Repository.prototype.removeIndex = function(index) {
  if (items[index]) {
    if (/* items[i] has more than 1 reference to it */) {
      // Only remove item from repository if nothing else references it
      this.items.splice(index,1);
      return;
    }
  }
}  

注意remove 中带有注释的行。如果没有其他对象引用该项目,我只想从我的对象主存储库中删除该项目。这里是Collection

var Collection = function(repo,items) {
  this.repo = repo;
  this.items = items || [];
}
Collection.prototype.remove = function(id) {
  for (var i=0,len=this.items.length; i<len; i++) {
    if (items[i].id === id) {
      // Remove object from this collection
      this.items.splice(i,1);
      // Tell repo to remove it (only if no other references to it)
      repo.removeIndxe(i);
      return;
    }
  }
}

然后这段代码使用RepositoryCollection

var contactRepo = new Repository([
    {id: 1, name: "Joe"},
    {id: 2, name: "Jane"},
    {id: 3, name: "Tom"},
    {id: 4, name: "Jack"},
    {id: 5, name: "Sue"}
  ]);

var friends = new Collection(
  contactRepo,
  [
    contactRepo.get(2),
    contactRepo.get(4)
  ]
);

var coworkers = new Collection(
  contactRepo,
  [
    contactRepo.get(1),
    contactRepo.get(2),
    contactRepo.get(5)
  ]
);

contactRepo.items; // contains item ids 1, 2, 3, 4, 5 
friends.items;  // contains item ids 2, 4
coworkers.items;  // contains item ids 1, 2, 5

coworkers.remove(2);

contactRepo.items; // contains item ids 1, 2, 3, 4, 5 
friends.items;  // contains item ids 2, 4
coworkers.items;  // contains item ids 1, 5

friends.remove(4);

contactRepo.items; // contains item ids 1, 2, 3, 5 
friends.items;  // contains item ids 2
coworkers.items;  // contains item ids 1, 5

注意coworkers.remove(2) 没有从contactRepo 中删除id 2?这是因为它仍然是从friends.items 引用的。但是,friends.remove(4) 会导致 id 4 从contactRepo 中删除,因为没有其他集合引用它。

总结

以上是我想做的。我确信有办法通过跟踪我自己的引用计数器等来做到这一点。但是如果有办法使用 javascript 的内置引用管理来做到这一点,我想听听如何使用它。

【问题讨论】:

  • 如果删除是你的唯一目的,GC 就是这个任务的对象。他会/应该完全按照你的描述去做。
  • @Itay:当没有Collection 实例引用它时,我想从Repository 中删除一个项目。所以常规的垃圾回收在这里不起作用,因为该项目仍然存在于Repository 中。我必须知道某个对象是否仍在某处使用,这就是为什么我创建了一个存储库对象来包含对所有正在使用的对象的引用。
  • 那么你需要编写一个全局函数来替换 = 运算符并在其中构建一个....引用表?而且您还需要编写某种垃圾收集器。都可以在JS中完成。虽然我想知道这一切是否真的有必要。如果你有“被遗忘的物品”,你在乎什么?
  • @Itay:谢谢,没有考虑过重载=,但不确定我会走那条路。我意识到这听起来可能过于复杂,但我有一个要求,即数据元素在 UI 上显示的任何位置都必须准确反映实际值。因此,如果我有 3 次显示相同的联系人,如果其中一个发生变化,其他人也必须立即更改。我正在构建一个事件驱动系统来执行此操作,但我发现我需要确保所有 UI 元素都绑定到相同的数据模型,如果我可以跟踪引用计数,那将是最简单的。
  • 你的问题很重要,有人知道有什么调试器可以显示这种信息吗? Chrome 调试器和 Firebug 没有。把它放在调试器上总比不知道要好。

标签: javascript garbage-collection reference


【解决方案1】:

不,不,不,不;是的,如果你真的需要计算引用,你将不得不手动进行。 JS 没有 this、GC 或弱引用的接口。

虽然您可以实现手动引用计数对象列表,但是否所有额外开销(在性能方面,但更重要的是代码复杂性)是否值得值得怀疑。

在您的示例代码中,忘记Repository 似乎更简单,为您的列表使用简单的Array,并让标准垃圾收集处理丢弃未使用的人。如果您需要获取所有使用人员的列表,您只需 concat friendscoworkers 列表(如果需要,还可以对它们进行排序/唯一化)。

【讨论】:

  • 感谢您的反馈。我担心没有原生的javascript方法来做到这一点。我意识到这可能看起来很臃肿,但我正在使用一个非常大的应用程序,这是一个过于简化的示例。在应用程序中,跟踪所有不同的friendscoworkers 和其他相关数组几乎是不可能的。此外,还有许多不同类型的对象,而不仅仅是联系人。因此,为每种类型都有一个存储库实际上会使事情变得更简单。这一切都与数据绑定解决方案有关,该解决方案在模型更改时触发事件。
  • 您知道是否有任何方法可以在调试器(如 firebug 或 chrome web 检查器)上获取引用计数
【解决方案2】:

您可能有兴趣研究 reduce 函数和 array.map 函数。 map 可用于帮助确定您的集合相交的位置,或者是否存在相交。用户定义的reduce函数可以像合并一样使用(有点像覆盖加法运算符,以便您可以将操作应用于对象,或者如果这是您定义reduce函数的方式,则合并“id”上的所有集合 - 然后将结果分配给您的主参考数组,我建议保留一个包含所有根对象/值的影子数组,以防您想要 REWIND 或其他东西)。注意:在减少对象或数组时必须注意原型链。在这种情况下,地图功能将非常有用。

我建议不要删除存储库中的对象或记录,因为您以后可能想再次引用它。我的方法是创建一个 ShadowRepository 来反映至少具有一个“参考”的所有记录/对象。从您在此处提供的描述和代码看来,您正在初始化所有数据并存储对 1、2、4、5 的引用,如代码中所示。

var contactRepo = new Repository([
    {id: 1, name: "Joe"},
    {id: 2, name: "Jane"},
    {id: 3, name: "Tom"},
    {id: 4, name: "Jack"},
    {id: 5, name: "Sue"}
]);
var friends = new Collection(contactRepo,[
    contactRepo.get(2),
    contactRepo.get(4)
]);

var coworkers = new Collection(contactRepo,[
    contactRepo.get(1),
    contactRepo.get(2),
    contactRepo.get(5)
]);

从存储库和集合的初始化开始,您所要求的“如果没有对它的引用,则从存储库中删除项目”第 3 项需要立即删除。但是,您可以通过几种不同的方式跟踪参考。

我考虑过使用 Object.observe 来处理类似的情况。但是,Object.observe 并不适用于所有浏览器。 I have recently turned to WatchJS

我正在努力理解 Watch.JS 背后的代码,以允许动态创建对象上的观察者列表,这将允许人们也删除不再被监视的项目,尽管我建议删除参考访问点 - 我的意思是一个变量,它与一个对象共享直接词法范围,该对象已经给它的兄弟提供了一个单一的引用点,可以被删除,使其在暴露记录/项目的对象之外不再可访问/property/object 它的兄弟姐妹。随着您的所有其他引用依赖于已删除的对基础数据的访问的引用被停止。我正在为原始引用生成唯一 id,以避免意外重用同一个。

感谢您分享您的问题和您正在使用的结构,它帮助我考虑了我自己的一个具体案例,我正在生成对词汇同级的唯一标识的引用,这些唯一 ID 保存在具有范围的 ONE 对象上, 读完这里后,我重新考虑并决定只公开一个引用,然后将该引用分配给需要它的变量名称,例如创建观察者或观察者或其他集合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2017-06-08
    • 1970-01-01
    相关资源
    最近更新 更多