访问对象的父级

3

好的,首先我知道对象除非明确定义否则没有对其容器的引用,因此我正在寻找一个解决方法。

请看以下代码(为了易读性大幅简化了我的用例):

var cid = 0;
var Command = function(c) {
    this.id = cid += 1;
    this.transient = false;
    return this;
}

var sid = 0;
var CommandSet = function() {
    this.id = sid += 1;
    this.commands = [];
    this.transients = 0;
    return this;
}

CommandSet.prototype.parent = null;
CommandSet.prototype.recurse = function(callback) {
    callback.call(this);
    if (this.parent instanceof CommandSet) {
        this.parent.recurse(callback);
    }
}

CommandSet.prototype.createSubset = function() {
    var set = new CommandSet();
    set.parent = this;
    set.commands = this.commands;
    set.transients = this.transients;
    return set;
}


CommandSet.prototype.addCommand = function(c) {
    if (c instanceof Command) {
        this.commands.push(c);
        if (c.transient) {
            this.recurse(function() {
                this.transients++;
            });
        }
    }
    return this;
}

CommandSet.prototype.toggleTransient = function(c) {
    if (c instanceof Command) {
        c.transient = true;
        this.recurse(function() {
            this.transients++;
        });
    }
    return this;
}

如果我按照以下步骤操作(http://jsfiddle.net/5KGd8/1/):
var s1 = new CommandSet();
var c1 = new Command();
var c2 = new Command();
s1.addCommand(c1).addCommand(c2);

var s2 = s1.createSubset();
var s3 = s1.createSubset();
s2.toggleTransient(c1);

console.log(s1);
console.log(s2);
console.log(s3);

s1现在有一个瞬态,s2现在也有一个瞬态,但是s3仍然没有,尽管它包含对相同Command对象的引用。

可能的解决方案:

  1. 我可以为每个命令构建一个引用,其中存储了所有包含它的集合,并遍历这些集合,但这将导致一些严重的内存问题,因为我的应用程序的真实性质要求子集可以被垃圾回收(用户通常会匿名创建许多子集而不知道),而这将在使用后保留对它们的引用。 parent 引用很好,因为我希望父集存在,只要它有一个存活的子集。

  2. 我可以明确地强制用户在不再需要子集时运行删除函数,该函数将删除所有内部引用,但这会使事情变得复杂,并且我希望事情能够自动完成。我的应用程序的性质意味着用户可能在他们甚至没有意识到的情况下创建子集(通过创建和执行子集的其他功能)。

有人能想出一种解决这个问题的方法,而不会出现我两个解决方案中描述的问题吗?


@BenjaminGruenbaum 没有理由...完全没有理由。 - George Reith
commands 是一个数组,你只是复制了它的引用,但 transients 是一个数字,它真正被复制了。 - jantimon
@BenjaminGruenbaum 什么是瞬态并不重要,它可以是香蕉等等,它只是Command对象上的一个布尔值。我希望所有包含Command对象的集合都能够具有最新的计数,即包含一个瞬态值为true的Command对象的数量。 - George Reith
@jantimon 之所以复制瞬态是因为这样更快,在我的用例中我会计算它,但在这里不可能创建一个具有不同数量的瞬态“Command”对象的子集。在我的用例中,我不想通过引用传递它,因为子集可能包含与其父级不同数量的瞬态(仅包含其父级包含的某些命令)。 “Command”对象是有意按引用传递的。 - George Reith
1
你能否使用Object.defineProperty的第二个参数将transient转换为getter和setter函数对吗?你可以省略setter,但getter始终会通过this.commands迭代返回正确的数量。如果不能,则可以将transient转换为返回transient命令的函数(不确定是否会极大地影响性能)。 - HMR
显示剩余2条评论
1个回答

1

抱歉,这不是一个答案,但我想确保我理解了问题。

CommandSet可以有Commands,当您更改Command的瞬态属性时,您希望包含该Command的CommandSet具有更新的瞬态计数器。

如果故事到此结束,您可以简单地使Command维护CommandSet列表,以便更新其容器。

然而,这不起作用,因为您可能会在函数中创建CommandSets,当这些CommandSets超出范围时,它们不会被垃圾收集,因为它们包含的Command会引用它们。这些命令不会随着CommandSets超出范围而消失,因为它们还包含在其他(全局)CommandSets中。

重新分配原始类型(瞬态)不会重新分配子集或主集中的原始类型,但如果瞬态不是原始类型呢?

在构造函数中:

this.transients = {count:0};

在createSubset中
set.transients = this.transients

在 toggleTransient 中。
this.transients.count++; or --

无论您在子集还是主集中操纵瞬态,只要使用toggleTransient,它就会更改所有的计数。

谢谢,你对这个问题有很好的掌握。虽然我在问题中过于简化了我的问题,并忘记提到一个子集可能具有不同数量的瞬态与其父集(在我实际的用例中,子集通常只包含一些父命令)。通过引用将瞬态计数传递给子集的问题在于它们将始终具有与父节点相同的瞬态计数,反之亦然。 - George Reith
@GeorgeReith 在CommandSets的有效期上有时间限制吗?比如说,如果它们在一定时间内没有被使用,您可以安全地说它们已经过期,并且对/来自其命令的引用可能会被清除? - HMR
很抱歉,这完全取决于用户何时允许CommandSet超出范围而不再相关。请注意,此处的用户是另一位程序员;这是我正在构建的库的一部分。 - George Reith
@GeorgeReith 即使你创建一个对象,使用CommandSet ID和Command ID更新CommandSets中的某些总数,当CommandSet超出范围时,这些状态将无用地保留。由于在对象垃圾回收之前不会通知对象,因此无法告诉“CommandSetState”对象删除该CommandSet的详细信息。泄漏会减少,但仍然有泄漏。我很好奇是否有人能找到一种解决方法。 - HMR
事实上,如果JavaScript有弱引用,那将解决我所有的问题。我从未研究过它,但我认为事件监听器创建强引用,否则可能可以从集合中注册事件监听器到它们的命令。虽然这不是一个可行的答案,但因其有帮助性而被点赞。 - George Reith
@GeorgeReith 为了使事件起作用,它们必须知道要调用侦听器方法的实例,通常通过闭包完成。这就是我问他们是否会过期的原因。当过期时间过去时,闭包可以取消引用特定的CommandSet实例。 - HMR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接