34

At some point in the past, I read something that gave me the idea that anonymous functions in JavaScript can use up a surprising amount of memory (because they carry the entire current scope around with them), whereas named (static?) functions don’t have this issue.

I can’t remember where I read this, so I can’t go back and re-read it and figure this out for myself.

I’ve got two questions:

  1. Are there situations where anonymous functions can use enough memory for it to be worth caring about? (If so, do you have an example?)
  2. Are there any other drawbacks to anonymous functions (as opposed to named/static functions)?
Suraj Jain
  • 4,463
  • 28
  • 39
Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
  • Excellent question. I've used up my votes for the day - can someone else +1 for me? – Michael Berkowski Jul 14 '11 at 18:49
  • @Michael Already did. Great question. – Craig M Jul 14 '11 at 18:49
  • There have been similar question on SO about the *implementation* of closures, but I have yet to see one with a satisfactory answer. That is, are *modern* JavaScript engines smart enough to only keep around objects in the [[scope]] which *can* be accessed? Obviously this optimization is not practical to apply in presence of `eval`, etc... –  Jul 14 '11 at 18:50
  • 1
    F—ing hell, 8 upvotes in four minutes? I assumed I was being dumb. – Paul D. Waite Jul 14 '11 at 18:52
  • 3
    Actually, *every* function, whether anonymous or not, declaration or expression, has access to each higher scope. So you could generalize the question and ask whether functions in JavaScript have a memory issue or maybe better: What are the memory implications if functions are passed out of the scope they are defined in (which is then about closures I guess). – Felix Kling Jul 14 '11 at 18:54
  • Similar question here: http://stackoverflow.com/questions/80802/does-use-of-anonymous-functions-affect-performance – Paul D. Waite Jul 14 '11 at 18:57
  • @FelixKling That what was I thought on reading the question, why would anonymous function be any different, every function has its scope in it. – Suraj Jain Jan 05 '20 at 15:28

3 Answers3

8

All JavaScript functions will behave in the same manner in that they inherit the variable environments in entire scope chain leading up to, and including, themselves. This is equally true for both anonymous and named functions.

This chain of references to the outer environments stays with each function, even if the function is passed into an entirely different scope.

Traditionally, this would mean that all variables in any given chain have a reference retained to them as long as the inner closure continues to exist. Although in modern browsers that compile the code, it is likely that there will be an analysis of which variables are actually referenced, and only those will be retained, allowing others that are no longer referenced to be garbage collected.

However, there are other situations where an anonymous function is wasteful.

Here's a common bit of code:

for( var i = 0; i < 100; i++ ) {
    (function( j ) {
        setTimeout( function() { console.log( j ); }, 1000 );
    })( i );
}

This is a situation where an anonymous function is a bit more wasteful than a named function because you're recreating an identical function 100 times during the loop when you could just reuse a named one.

function setConsole( j ) {
    setTimeout( function() { console.log( j ); }, 1000 );
}

for( var i = 0; i < 100; i++ ) {
    setConsole( i );
}

This has the exact same closure overhead, but is more efficient because you've only constructed one function to create each new variable environment.

http://jsperf.com/immediate-vs-named (Thanks to @Felix Kling for the jsPerf.)

So with respect to the closure in particular, yes there's overhead as long as you maintain the closure by some permanent reference. I'd say that it is good to avoid this if possible but not to be obsessive about it. Sometimes a new variable environment added to the scope chain is simply the best solution.


EDIT: Here's an article from Google. Specifically, see Avoiding pitfalls with closures. for information on the performance impact of extending the scope chain, and for a claim that anonymous functions are “slower” than named functions.

Community
  • 1
  • 1
user113716
  • 318,772
  • 63
  • 451
  • 440
  • 1
    **Moderator Note** _Comments under this answer were removed because they degenerated into more noise than signal. Please keep comments constructive, professional and most of all on topic._ – Tim Post Jul 16 '11 at 18:08
  • 1
    Thanks to @Tim Post for the removal. The result of the relevant discussion with OP, as well as the performance test provided by @Felix Kling has been added to the answer. – user113716 Jul 16 '11 at 18:12
  • @user113716 Can you explain this line ` more efficient because you've only constructed one function to create each new variable environment`? – Suraj Jain Jan 05 '20 at 15:32
  • @user113716 Suppose there was a variable `a` outside of `setConsole` used by function passed to `setTimeout`, shouldn't the memory only matter then, and not in this case? I mean how is one case more efficient than other in current setup. – Suraj Jain Jan 05 '20 at 15:48
3

I think what you probably read about was the IE closure memory leak problem.

Look at this article on the issue.

Basically, on older versions of IE, the garbage collector could not handle circular references involving DOM objects. Since closures are very conducive to such circular references, they could easily lead to annoying memory leaks in IE.

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • Yeah, I’m not sure it was that — I do remember the IE circular references thing, and that it had to do with closures (I could never quite get the full details of it to stick in my head), but I *think* the thing I read was a more general point about anonymous functions. – Paul D. Waite Jul 14 '11 at 18:54
0

This is true, due to closures that are created. as a general rule the biggest issue with the practice is performance concerns with IE (especially older versions of IE), which has a terrible track record of dealing with these properly.

The Brawny Man
  • 661
  • 6
  • 10