如何获得 javascript 对象引用或引用计数?

如何得到一个对象的引用计数

  • 是否可以确定一个 javascript 对象是否有 多个参考文献
  • 或者它是否有参考 除了我正在访问的那个
  • 或者只是为了得到 参考计数本身?
  • 我可以从 javascript 本身找到这些信息吗? 或者我需要跟踪我自己的引用计数器。

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

如果你知道答案,就没有必要阅读这个问题的其余部分。下面只是一个例子,让事情变得更清楚。


用例

在我的应用程序中,我有一个名为 contactsRepository对象实例,它包含一个 全部联系人数组。还有多个 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)会导致从 contactRepo中删除 id4,因为没有其他集合引用它。

摘要

以上就是我想做的。我相信我可以通过跟踪自己的参考计数器之类的方法来做到这一点。但是,如果有一种方法可以使用 javascript 的内置引用管理来实现这一点,我想听听如何使用它。

35521 次浏览

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

虽然 可以实现了一个手动引用计数的对象列表,但是所有额外的开销(从性能角度而言,但更重要的是代码复杂性)是否值得还是个问题。

在您的示例代码中,似乎更简单的做法是忘记 Repository,对列表使用普通的 Array,并让标准的垃圾收集来处理删除未使用的人员。如果您需要获得所有正在使用的人的列表,那么只需要 concat friendscoworkers列表(如果需要,还可以对它们进行排序/唯一化)。

您可能有兴趣研究 reduce 函数和 array.map 函数。Map 可以用来帮助识别集合的交叉点,或者是否存在交叉点。用户定义的 reduce 函数可以像 merge 一样使用(有点像覆盖加法运算符,这样你就可以对对象应用操作,或者如果你定义 reduce 函数的方式是在“ id”上合并所有集合——然后将结果赋给你的主引用数组,我建议保留一个影子数组,它保存所有的根对象/值,以防你想要 REWIND 或其他东西)。注意: 在减少对象或数组时,必须小心原型链。在这种情况下,map 函数将非常有帮助。

我建议 没有删除存储库中的对象或记录,因为您可能希望以后再次引用它。我的方法是创建一个 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.Observer

我正在理解 Watch.JS 背后的代码,以允许动态创建一个对象的观察者列表,这将允许一个人也删除一个不再被监视的项目,虽然我建议删除访问点的引用-我的意思是,一个变量,共享的直接词法范围与一个对象,已经给出了一个单一的引用它的兄弟姐妹可以被删除,使它不再可访问的对象外,已经公开的记录/项目/属性/对象的兄弟姐妹。如果所有其他引用都依赖于对基础数据的已移除访问,则停止该引用。我正在为原始引用生成唯一的 id,以避免意外地重用同一个 id。

感谢你分享你的问题和你正在使用的结构,它已经帮助我考虑我自己的一个特定的情况下,我正在生成唯一识别的引用到一个词汇的兄弟姐妹这些唯一的 id 保存在一个对象有作用域,在这里阅读后,我已经重新考虑,并决定只公开一个引用,然后分配给一个变量名称在任何需要它的地方,如在创建一个观察者或观察者或其他集合。