Difficulty: Easy

preface

I often discuss variable storage in closures with candidates in interviews, and find that most of them only stop at the definition of closures, but are vague about variable storage in closures. Therefore, this paper will try to find out through heapdump analysis of JS engine running time.

An example closure

Let’s open Chrome and run the following code:

function makeStevenFunc() {
    var stevenx911_name = new Array(1000000).join('x'); // Construct a 1MB string from an array

    function displayStevenName() { // define a named function to make it easier to find
        console.log(stevenx911_name);
    }
    return displayStevenName;
}

var myFunc = makeStevenFunc();
myFunc();
Copy the code

Open DevTools, switch to the Memory screen, and click Take Heap Snapshot to obtain the memory snapshot of the current page

Heapdump is a snapshot of the heap, and the base string variable stevenx911_name defined in the closure is stored in the heap.

But do all variables defined by makeStevenFunc go into closures? Let’s change the code to continue:

function makeStevenFunc() {
    var stevenx911_name = new Array(1000000).join('x'); 
	var stevenx911_desc = new Array(1000000).join('y'); // Add this sentence, also construct a 1MB size string
    function displayStevenName() {
        console.log(stevenx911_name);
    }
    return displayStevenName;
}

var myFunc = makeStevenFunc();
myFunc();
Copy the code

Heapdump results:

The answer seems obvious: the size of heapdump does not change, and the closure formed by the sample code does not contain all the variables defined in the parent function, only the variables referenced by the child function.

At this point, it’s pretty clear where variables are stored in closures! If you are careful, you will notice that the heapdump operation is performed at the end of the above code execution, but the variable space in the closure is not freed, and the garbage collector does not seem to be able to reclaim it, so let’s talk about the problem caused by closures: memory leaks.

Memory leaks caused by closures

Closures are a common source of memory leaks in JS, so be careful when using closures and do some testing. Here is an example of code that causes a memory leak:

var funs = [];
function fun0(){
    funs.push(getVar());    // The external variable holds a reference to the closure
}
setInterval(fun0, 1000);
function getVar(){
    var arr = new Array(1000000);
    return function(){
        console.log(arr); }}Copy the code

After executing this code in the browser, we observed the Proformance Monitor and saw that the JS Heap Size continued to grow and the memory footprint increased because the external variable (global) held a reference to the closure, and if this happened on the page, the page would freeze when the memory usage ran out. This can be done by placing the memory allocation operation (new) outside the closure or by destroying it when it is done (assigning null).

Of course, the reality may not be timers, but rather user actions, such as onclick and onResize, which are frequently triggered by event callbacks.

conclusion

There are many articles on the Internet about variable storage in closures. There are many diagrams and detailed theories. If you want to know more, you can open the reference link in the reference section of this article for further reading. The ECMA specification is the first authority, followed by the implementation of the specification – the JS engine, and then the various video blogs, so this article aims to give you a superficial understanding of variable storage in closures at the engine level from a runtime perspective. (after)

reference

Developer.mozilla.org/zh-CN/docs/…

Developers.google.com/web/tools/c…

Juejin. Cn/post / 684490… — Very detailed

Cloud.tencent.com/developer/a… — Very detailed