14

Some of coworkers are saying that nesting functions is bad for performance and I wanted to ask about this.

Lets say I have the function:

function calculateStuff() {
    function helper() {
    // helper does things
    }
    // calculateStuff does things
    helper();
}

helper is a private function that is only used inside calculateStuff. That's why I wanted to encapsulate this inside calculateStuff.

Is this worse performance wise than doing:

function helper() {

}

function calculateStuff() {
    helper();
}

Notice that in the second case, I expose helper to my scope.

2
  • I'm voting to close this question as off-topic because performance metrics are best handled by jsperf and have a short shelf-life as browsers evolve. Commented Oct 20, 2015 at 23:20
  • 1
    Don't ask others about performance. When in doubt, measure it. Commented Oct 20, 2015 at 23:22

2 Answers 2

13

In theory, there's a potential performance impact, in that you need to create a new closure context for helper every time calculateStuff is called (because it might reference variables from the enclosing scope).

I'm pretty sure that the JIT compiler in most JavaScript engines should be able to tell that you aren't actually accessing any variables from the parent context, and just skip binding all of those values. I may be missing some edge case where this isn't generally possible, but it seems straighforward-enough.

In any case, we're talking about nanoseconds of overhead per iteration, so unless your code is executed a lot, you'd never notice the time difference. If in doubt, profile it and check...


I decided to follow my own advice, and profile this on jsperf, with Safari 9. I used the do-nothing functions as provided in the original question, to highlight the overhead of just calling a nested function:

Nested functions: 136,000,000 calls per second

Flat functions: 1,035,000,000 cals per second

Oriol's IIFE version: 220,000,000 cals per second

Clearly the flat functions are much faster than either of the alternative versions. However, think about the magnitude of those numbers - even the "slow" version only adds 0.007 microseconds to the execution time. If you do any kind of calculation or DOM manipulation in that function, it'll absolutely dwarf the overhead of the nested function.

Sign up to request clarification or add additional context in comments.

8 Comments

"to create a new closure context for helper every time calculateStuff is called (because it might reference variables from the enclosing scope)." --- so if it does not refer to any free variables - there is no such impact at all? If that's what you're trying to express, then it's wrong.
@zerkms: Yes, if it does not reference free variables, and especially if it is not exported from that scope, the function can and will be inlined. How is that wrong?
@zerkms: Neither me nor Mark said so. Of course, the standard does not require anything about performance (except ES5 Maps&Sets), but Mark is right that most JIT compilers (i.e., actual implementations) are able to skip over creating the function object or even a closure context on the heap.
@Bergi right, but it implies that binding a lexical scope is the only overhead there. Whereas the function creation in general is not cheap at all. So my point here is that the answer emphasizes on binding of scope and misses an important part of creating a function object.
@zerkms: In any case, the answer should be upvoted for "If in doubt, profile it" alone :-)
|
5

With your first code, at each call of calculateStuff, a new copy of helper will be created.

With your second code, all calls will share the same helper, but it will pollute the outer scope.

If you want to reuse helper without polluting the outer scope, you can use an IIFE:

var calculateStuff = (function () {
  function helper() {
    // helper does things
  }
  return function() {
    // calculateStuff does things
    helper();
  }
})();

7 Comments

You'd still be polluting the outer scope, although it wouldn't be the global scope.
@MinusFour polluting it with what exactly?
@MinusFour It creates an intermediate scope and pollutes that instead of the outer one.
I'm just saying that the intermediate scope could also be considered an outer scope (at least from anonymous function context).
@MinusFour that makes no sense.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.