"Unclosure" a closure

Asked

Viewed 243 times

8

I understand the great powers and the great consequences of the closures.

But suppose I have the following code...

function foo () {
    var x = 1;
    function bar () {
        return x++;
    }
    return bar;
}

... And that I don’t wish to maintain the context of foo in memory, because in real life it can be a function (or several) that uses a very large amount of memory. But still, I wish to use the values processed by foo in the context of bar.

If it were x a global variable, would a closure still be formed? Case x were external to foo, as in...

var x = 1;
function foo () {
    function bar () {
        return x++;
    }
    return bar;
}

...There would still be closure?

Is there any sane way to achieve the goal I described earlier? And regardless, is there any way to destroy an execution context that I no longer need to use?

  • You want a x place to foo that does not interfere with x global?

  • @Andréleria I would like to copy the values of the variables of foo into bar... to have the values as processed by foo, but not hold a running context in memory.

  • I just didn’t understand the last part of holding a context. Your concern is memory?

  • @Exactly. I want to use the absolute minimum of memory possible every moment.

  • 1

    I understand you, and I’ve been in your shoes. I’m hoping for good answers, but after a while researching answers of this kind, my conclusion is that if you want good memory usage, use C/C++/ASM. But like I said, I hope you get a good solution, because I’ve been looking for you for a while too. :)

3 answers

6


From what I understand, you need to part of the variables of a given execution context, but does not want to keep them all in memory. A solution is to generate its function in another context, passing as parameter the data you want to keep:

function foo () {
    var x = 1;
    var naoQuero = 2;
    return criaBar(x);
}
function criaBar(x) {
    return function() {
        return ++x;
    }
}
var bar = foo();
bar(); // retorna 2
bar(); // retorna 3

But attention: the x within the returned function will be a copy of the original, at the time it was passed to criaBar. No use trying to change it inside foo, after the call to criaBar, and hope that bar() increment this changed value.

And notice I changed the return x++ for return ++x, otherwise the code returns the current value and then increments, and I imagine the goal is to return the already incremented value.

4

Javascript (JS) does not have an explicit memory manager, the browser decides when to clean it. Sometimes there may be a loss of efficiency in JS rendering due to a pause for garbage collection (GC - Garbage Collection).

There are techniques to overcome failures caused by GC.

Suppose you have an algorithm written in JS, and every second you are creating a new object, it is obvious that after a certain amount of time the GC will act freeing more space for your application.

For real-time applications that require a lot of space, the simplest thing you can do is reuse the same memory. It depends on how you structure your code. If it generates too much junk, then it can over time experience a slowness brought about by GC., here I list 3 initial study items to improve your code.

Use simple procedures: Whenever possible, you should try to reuse the same object by adding or modifying its properties. Object recycling is a very good way to avoid always creating new objects by filling the "memory" of the browser.

In the case of array: it is usually assigning [] in order to remove it from memory, but it is a mistake, because it creates a new matrix and leaves the old one as garbage. To reuse the same array you must use vararray.Length = 0 This has the same effect as the previous one but reuses the array object instead of creating a new one. You can still use the operator delete that I will explain further below.

In case of functions: Sometimes it is necessary to call a specific function at a given time or in intervals using setInterval or setTimeout.

setTimeout(function() { function(){ ... código ... } }, 10);

You can optimize this code by assigning the function to a permanent variable instead of creating it at each interval.

var minhafuncao = function(){ ... código ... }
setTimeout(minhafuncao, 10);

Be careful when using the functions slice() this creates a new array leaving the previous one untouched, substr() also leaves the initial string intact, generating a new one in "memory", there are still others (that does not come to mind) that generate new objects leaving the previous ones occupying space, forcing the GC to enter into activity more often.

You still have the option to use the operator delete which serves to remove properties of objects:

x = 42;         // craindo uma propriedade C do objeto global
var y = 43;     // declarando uma nova variável 'y'
meuObj = {
    h: 4,
    k: 5
};

delete x;        // retorna true  (x é uma propriedade do objeto global e pode ser deletado)
delete y;        // retorna false (delete não funciona com variáveis)
delete Math.PI;  // retorna false (delete não funciona com propriedade pré definidas)
delete meuObj.h; // retorna true  (propriedade definida por usuário pode ser deletada)

delete meuObj;   // retorna true  (meuObj é uma propriedade do objeto global, não é uma variável, nesse caso pode ser deletado)

Another example now using prototype

function Foo(){}
Foo.prototype.bar = 42;
var foo = new Foo();
delete foo.bar;           // retorna true, mas não tem efeito, bar é uma propriedade herdada
console.log(foo.bar);     // mostra 42, propriedade herdada
delete Foo.prototype.bar; // deleta propriedade no prototype
console.log(foo.bar);     // mostra "undefined"

Relative to the array when you remove an element, the length of the array is not affected. This is true even if you delete the last element. When the delete operator removes an element from the array, that element is no longer accessible. In the following example, myArray[3] is removed with delete.

var meuArray= ["zero","um","dois","três","quatro","cinco"];

delete meuArray[3];
if (3 in meuArray) {
    //esse bloco nunca será executado
}

If you want an element of the matrix to not cease to exist, but only have an undefined value, use undefined instead of the operator delete. In the following example, meuArray[3] is assigned undefined, but the array element still exists:

var meuArray= ["zero","um","dois","três","quatro","cinco"];

meuArray[3]=undefined;
if (3 in meuArray) {
    // esse bloco será executado
}

Avoiding garbage in JS is an art, I would say that is (at least today) impossible, but browsers have evolved to improve the collata of these. What we have to keep in mind is that all code must be done in order to optimize the performance of the system so that after thousands of lines it is not necessary to redo it.

  • 2

    This is a good answer, which comments on several relatively complex issues. I just don’t know if it answers what was asked.

3

Yes, do not use closures. Use simple objects with NEW or literal objects with {}. The closure, by definition, will maintain the entire scope: the internal + the external function.

Your second solution is even worse, because it will ensure that the object has reference to the variable 'global' x, not allowing the GC to enter to clean it from memory.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.