factordog factordog - 1 month ago 7
HTML Question

Javascript: Remove reference to HTML selectors in prototype inheritence?

I am busy learning about prototype inheritance using objects. My code currently works as it should. But what I am now trying to do is avoid creating memory leaks.

I have a bunch of HTML selectors that are being called in the parent object. These are later used to remove and add some CSS animations.

What I am now wanting to do is to basically remove or make the parent constructor to become undefined and have its children have no reference to those selectors.

Here is a Code Share As I dont want to just dump it all here. But in question the parent is:

var GameStartup = function() {
this.welcomeStartBtn = document.querySelector('.game-startup__welcome a');
this.welcomeStartBtn__controller = document.querySelector('.game-startup__welcome a span:first-child');
this.welcomeStartBtn__copy = document.querySelector('.game-startup__welcome a span:last-child');
};


So basically I am wanting its child ShowLogin and the parent to be undefined or something so that it no longer is able to create things like detatched dom nodes etc.

Answer

One thing you can do is the use of getters like so:

function GameSartup () {}

GameSartup.prototype = {
    constructor: GameSartup,

    get welcomeStartBtn () {
        return document.querySelector('.game-startup__welcome a');
    }

    // more getters here
}

This way you might have some overhead, since you are querying the dom each time you are »getting« the DOM Element, but you remove all references as asked.

If this slows things down too much, you can create a cache for the element references like so:

function GameSartup () {
    this.domcache = {};
}

GameSartup.prototype = {
     constructor: GameSartup,

    get welcomeStartBtn () {
        var selector = '.game-startup__welcome a';
        if (!this.domcache.hasOwnProperty(selector)) {
            this.domcache[selector] = document.querySelector(selector);
        }
        return this.domcache[selector];
    },

    // more getters here

    trash: function () {
        this.domcache = null;
        delete this.domcache;
    }
}

This way you can free all references at once, but you still have to clear the cache manually. But if you create a GameSartup object like so:

var gs = new GameSartup();

and later:

gs = null;

and no other references to gs exist, the garbage collector will find that there are also no references to the domcache and delete that as well.

UPDATE

In response to the comment:

»Cache invalidation« is always a crucial point in an application and caching itself is something that can get complicated and a source of strange errors. But it can also something that really speeds up things. It depends on the application how complicated this should/have to be.

I this case, I would start out without cache and if the performance is good, you are done.

I think the next step could be as described above: clearing the whole cache at once, at certain events. The method could look like:

//you do not need to loop over the cache and set the values to undefinded
//the garbage collector will free memory
//as soon as there is no reference to a given object left
clearCache: function(){
    this.domcache = {};
    return this;
}

The next step is to implement a »per item cache« like so:

addToCache: function (key, value) {
    if (!this.domcache.hasOwnProperty(key)) {
        this.domcache[key] = value;
    }
    return value;
}

clearFromCache(key) {
    delete this.domcache[key];
    return this;
}

Clearing items from cache, just after they have been added is useless, since this is just no cache plus the overhead of managing it.

All in all it depends on a whole bunch of factors like:

  • how often do you need to query an element,
  • will that element always reside in the dom, so one can query it always from the dom,
  • how many elements need to be cached,
  • how much code is needed to properly cache those elements,
  • how many elements are in the dom

In case of doubt, you should do some benchmarks with and without cache, to find the effective take away.

UPDATE #2

Since your talking about DOM Elements - as long as an element is part of the DOM (has a parent node) - it wont be deleted by the garbage collector, since there are references to it (item in childNodes array).

So if you are afraid of a memory leak, caused by too many elements, created at runtime like so:

var elements = [];
for(var i = 0; i < 10000; i++) {
     var div = document.body.appendChild(document.createElement('div'));
     elements.push(div);
}

Than you have to do two things: remove each element elements[i] from the dom like:

elements[i].parentNode.removeChild(elements[i]);

and

elements = null; //or a new array