【发布时间】:2011-02-25 14:22:00
【问题描述】:
如何获取对象的引用计数
- 是否可以确定一个 javascript 对象是否有多个引用?
- 或者如果它有引用除了我正在访问它的那个?
- 或者甚至只是为了获取引用计数本身?
- 我能否从 javascript 本身中找到这些信息,或者我需要跟踪我自己的引用计数器。
显然,我的代码必须至少有一个对它的引用才能访问该对象。但我想知道是否有其他引用它,或者我的代码是否是唯一可以访问它的地方。如果没有其他对象引用它,我希望能够删除该对象。
如果您知道答案,则无需阅读此问题的其余部分。以下只是一个示例,以使事情更清楚。
用例
在我的应用程序中,我有一个名为 contacts 的 Repository 对象实例,其中包含 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;
}
}
}
然后这段代码使用Repository和Collection:
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