0

I'm making a class that will be recreated many times, and in order to save memory I need to thoroughly delete it. Basically I need to access its containing variable if possible.

Here's the example:

function example(){
  this.id=0;
  this.action=function(){alert('tost');}
  this.close=function(){ delete this;}
}

var foo=new example();

My question is:

How can I get access to the foo variable from within the example function so I can remove it?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
volchkov
  • 69
  • 7

3 Answers3

1

window.foo will access that global variable.

this.close=function(){ delete window.foo; }

However, I remember there is something fishy with global variables, delete and window, so you might want to do otherwise, and simply use window.foo = null; for example.

If you want to access a variable defined in another function, you'll want to read the answers to this SO question.

Since what you want is to allow the garbage collector to release that object, you need to ensure that there are no references left to the object. This can be quite tricky (i.e. impossible) because the code manipulating the object can make multiple references to it, through global and local variables, and attributes.

You could prevent direct reference to the object by creating a proxy to access it, unfortunately javascript doesn't support dynamic getters and setters (also called catch-alls) very well (on some browseres you might achieve it though, see this SO question), so you can't easily redirect all field and method (which are just fields anyway) accesses to the underlying object, especially if the underlying object has many fields added to it and removed from it dynamically (i.e. this.anewfield = anewvalue).

Here is a smiple proxy (code on jsfiddle.net):

function heavyobject(destroyself, param1, param2) {
    this.id=0;
    this.action=function(){alert('tost ' + param1 + "," + param2);};
    this.close=function(){ destroyself(); }
}

function proxy(param1, param2) {
    object = null;
    // overwrites object, the only reference to
    // the heavyobject, with a null value.
    destroyer = function() { object = null; };
    object = new heavyobject(destroyer, param1, param2);
    return function(fieldname, setvalue) {
        if (object != null) {
            if (arguments.length == 1)
                return object[fieldname];
            else
                object[fieldname] = setvalue;
        }
    };
}
var foo = proxy('a', 'b');
alert(foo("action")); // get field action
foo("afield", "avalue"); // set field afield to value avalue.
foo("action")(); // call field action
foo("close")(); // call field close
alert(foo("action")); // get field action (should be 'undefined').

It works by returning a function that when called with a single argument, gets a field on the wrapped object, and when called with two arguments sets a field. It works by making sure that the only reference to the heavyobject is the object local variable in the proxy function.

The code in heavyobject must never leak this (never return it, never return a function holding a reference to var that = this, never store it into a field of another variable), otherwise some external references may be created that would point to the heavyobject, preventing its deletion.

If heavyobject's constructor calls destroyself() from within the constructor (or from a function called by the constructor), it won't have any effect.

Another simpler proxy, that will give you an empty object on which you can add fields, read fields, and call methods. I'm pretty sure that with this one, no external reference can escape.

Code (also on jsfiddle.net):

function uniquelyReferencedObject() {
    object = {};
    f = function(field, value) {
        if (object != null) {
            if (arguments.length == 0)
                object = null;
            else if (arguments.length == 1)
                return object[field];
            else
                object[field] = value;
        }
    };
    f.destroy = function() { f(); }
    f.getField = function(field) { return f(field); }
    f.setField = function(field, value) { f(field, value); }
    return f;
}
// Using function calls
o = uniquelyReferencedObject();
o("afield", "avalue");
alert(o("afield")); // "avalue"
o(); // destroy
alert(o("afield")); // undefined
// Using destroy, getField, setField
other = uniquelyReferencedObject();
other.setField("afield", "avalue");
alert(other.getField("afield")); // "avalue"
other.destroy();
alert(other.getField("afield")); // undefined
Community
  • 1
  • 1
Suzanne Soy
  • 3,027
  • 6
  • 38
  • 56
  • But if it is not a global variable? – DerWaldschrat Aug 08 '12 at 13:10
  • that does not help me because the variable foo can be define by many names for example: var foo=new example; var test=new example(); in your case i cant close the test variable – volchkov Aug 08 '12 at 13:11
  • its one solution but calling it like that is a bit inconvenient so i figured out a simpler solution. as long as i define the variable as global i can access to it via window['foo'] so what i do is check all the variables on the scope and if any of them have window[i].id and window[i].action i delete them – volchkov Aug 08 '12 at 14:07
  • this scrip also seems to recreate the object every time it's used witch means that in object changes like changing the id wont stick and creating dynamic objects like moving elements will be problematic – volchkov Aug 08 '12 at 14:16
  • I think you misunderstood how this should be used. You call the `proxy` or `uniquelyReferencedObject` only **once** in order to create the object, then you access the fields like this : `o(fieldname)` to read the value of a field, and `o(fieldname, value)` to write in a field. The `object` local variable will be the same across calls made to an object created by the same `proxy` or `uniquelyReferencedObject` call. – Suzanne Soy Aug 08 '12 at 14:23
  • You can avoid the obj('field') syntax and get back obj.field and obj['field'] one some browsers, see [this SO question](http://stackoverflow.com/questions/994143/javascript-getter-for-all-properties), but that's just syntactic sugar, and it doesn't matter (unless you have some library code that you can't change and expect the "usual" way to access fields). – Suzanne Soy Aug 08 '12 at 14:26
  • i think you are right. but the second function seems right it will leave a very small trace and will be tricky to use but that's a good start.thanks! – volchkov Aug 08 '12 at 14:37
  • you could stick function references to the unique object that will act like shortcuts to the inner object. – volchkov Aug 08 '12 at 14:42
  • No, because although these functions will hold a reference (`object`) to the inner object, they all share the same reference (the `object` variable they are closed on), and when that variable is set to null, this change is visible to all functions, and none will hold a reference to the inner object any more. P.S. : I just fixed a (harmless) typo in `destroy()` in the second implementation. – Suzanne Soy Aug 08 '12 at 16:23
1

The truth is that you can not delete objects in Javascript.

Then you use delete operator, it accepts the property of some object only. So, when you use delete, in general you must pass to it something like obj.p. Then you pass just a variable name actually this means 'property of global object', and delete p is the same as delete window.p. Not sure what happens internally on delete this but as a result browser just skip it.

Now, what we actually deleting with delete? We deleting a reference to object. It means object itself is still somethere in memory. To eliminate it, you must delete all references to concrete object. Everythere - from other objects, from closures, from event handlers, linked data, all of them. But object itself doest have information about all this references to it, so there is no way to delete object from object itself. Look at this code:

var obj = <our object>;
var someAnother = {
       ...
       myObjRef: obj
       ...
 }
var someAnotherAnother = {
       ...
       secondRef : obj
       ...
}

To eliminate obj from memory you must delete someAnother.myObjRef and someAnoterAnother.secondRef. You can do it only from the part of programm which knows about all of them.

And how we delete something at all if we can have any number of references everythere? There are some ways to solve this problem:

  • Make only one point in program from there this object will be referenced. In fact - there will be only one reference in our program. and Then we delete it - object will be killed by garbage collector. This is the 'proxy' way described above. This has its disadvantages (no support from language itself yet, and necessarity to change cool and nice obj.x=1 to obj.val('x',1). Also, and this is less obvious, in fact you change all references to obj to references to proxy. And proxy will always remain in memory instead of object. Depending on object size, number of objects and implementation this can give you some profit or not. Or even make things worse. For example if size of your object is near size of proxy itself - you will get no worth.

  • add to every place there you use an object a code which will delete reference to this object. It is more clear and simple to use, because if you call a obj.close() at some place - you already knows everything what you need to delete it. Just instead of obj.close() kill the refernce to it. In general - change this reference to something another:

      var x = new obj; //now our object is created and referenced
      x = null;// now our object **obj** still im memory 
      //but doest have a references to it 
      //and after some milliseconds obj is killed by GC...
    
      //also you can do delete for properties
      delete x.y; //where x an object and x.y = obj
    

    but with this approach you must remember that references can be in very hard to understand places. For example:

      function func() {
         var x= new obj;// our heavy object
         ...
         return function result() {
             ...some cool stuff..
         }
      }
    

    the reference is stored in closure for result function and obj will remain in memory while you have a reference to result somethere.

  • It hard to imagine object that is heavy itself, most realistic scenario - what you have some data inside it. In this case you can add a cleanup function to object which will cleans this data. Let say you have an gigant buffer (array of numbers for example) as a property of the object, and if you want to free memory - you can just clear this buffer still having object in memory as a couple dozens of bytes. And remember to put your functions to prototype to keep instances small.

Karabur
  • 546
  • 3
  • 7
0

Here is a link to some very detailed information on the JavaScript delete operator.

http://perfectionkills.com/understanding-delete/

Jay Tomten
  • 1,657
  • 1
  • 14
  • 23
  • it's said there that delete cant delete functions so i need to acces the foo variable to delete it. that is waht i am trying to find out how to do – volchkov Aug 08 '12 at 13:17