3

Ok, first up I know an object has no reference to it's container unless explicitly defined, so I am looking for a work around here.

Take the following code (heavily simplified from my use case for readability):

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;
}

If I then do the following (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 now has 1 transient, s2 now has 1 transient but s3 still has none despite containing a reference to the same Command objects.

Possible solutions:

  1. I could build a reference into each command which stores all the sets it is located inside and iterate through those, however this is going to cause some serious memory issues as the real nature of my application requires that subsets can be garbage collected (The user will create a lot of them anonymously often without realising) and this will retain a reference to them after they have been used. The parent reference is fine as I want the parent set to exist as long as it has a surviving subset.

  2. I could explicitly force the user to run a delete function on a subset when it is no longer needed which would remove all internal references to it but this complicates things for them and I like things to work automagically. The nature of my application means I would like the user to create subsets at times where they may not even realise they have done so (Through other functions which create and perform on subsets).

Can anyone think of a way to solve this problem without the issues described in my two solutions?

7
  • Why are you return thising from the functions called as constructors? Commented Nov 22, 2013 at 10:52
  • @BenjaminGruenbaum no reason... no reason at all. Commented Nov 22, 2013 at 10:54
  • Ok, just to clarify - you'd like to figure out the number of transients of a command (not sure what that means) based on all the supersets it's a member of? Commented Nov 22, 2013 at 10:55
  • commands is an array and you are only copying the reference to it but transients is a number which is really copied Commented Nov 22, 2013 at 10:57
  • 1
    Can you use Object.defineProperty with the second parameter to turn transient into a pair of getter and setter functions? You can leave out the setter but a getter would always return the right amount as it's iterates through this.commands. If you can't then maybe turn transient into a function that returns transient commands (not sure if that will greatly affect performance) Commented Nov 22, 2013 at 11:58

1 Answer 1

1

Sorry, this is not an answer but want to make sure I understand the problem.

A CommandSet can have Commands, when you change a Command's transient property you would like the CommandSet(s) that contain that Command to have an updated counter of transient the Commands it contains.

If the story ends here you could simply have Command maintain a list of CommandSet that the Command is in and update it's containers.

This would not work however because you would have CommandSets created in a function and when these go out of scope they won't be garbage collected because the Command(s) they contain would hold a reference to them. These commands would not go out of scope with the CommandSets because they are also contained in other (global) CommandSets.

Re assigning a primitive type (transients) does not re assign that in the subset or main set, but what if transients was not a primitive?

In the constructor:

this.transients = {count:0};

In createSubset

set.transients = this.transients

In the toggleTransient

this.transients.count++; or --

No matter if you fiddle with transients in subset or main set, as long as you use the toggleTransient it'll change count for all.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, you have a good grip of the problem. Although I oversimplified my problem in the question and forgot to mention that a subset could have a different number of transients to it's parent set (In my actual use case subsets normally contain only some of their parent's commands). The problem with passing the transient count via reference to the subsets is they will always have the same transient count as the parent and vice versa.
@GeorgeReith Is there a time limit on the validity of the CommandSets? Like when they're not used within a certain amount of time you could safely say they're expired and references to/from their commands could be cleaned up?
I'm afraid not. It is totally up to the user as to when the CommandSets are no longer relevant by allowing them to drop out of scope. Note the user in this case is another programmer; this is part of a library I am building.
@GeorgeReith Even if you create one object that uses CommandSet id's and Command id's to update certain totals in CommandSets These states will be uselessly retained when a CommandSet goes out of scope. Since an object is not notified when it's going to garbage collected you can't tell the "CommandSetState" object to delete the details for that CommandSet. You'll leak less but it's still leaking. I'm curious if anyone finds a way out of this one.
Indeed, if only JavaScript had weak referencing - that would solve all my issues. I've never looked into it but I assume event listeners create strong references, otherwise it may be possible to register event listeners from sets to their commands. Although not a working answer up-voted for helpfulness.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.