30

Specifically, I'm writing in JavaScript.

Let's say my primary function is Function A. If Function A makes several calls to Function B, but Function B is not used anywhere else, then should I just place Function B within Function A?

Is that good practice? Or should I still put Function B at the same scope as Function A?

32

I'm generally in favor of nested functions, especially in JavaScript.

  • In JavaScript, the only way to limit a function's visibility is by nesting it inside another function.
  • The helper function is a private implementation detail. Putting it at the same scope is akin to making a class' private functions public.
  • If it turns out to be of more general use, it's easy to move out, because you can be confident the helper is currently only used by that one function. In other words, it makes your code more cohesive.
  • You can often eliminate parameters, which makes the function signature and implementation less verbose. This is not the same thing as making variables global. It's more like using a class member in a private method.
  • You can give the helper functions better, simpler names, without worrying about conflicts.
  • The helper function is easier to find. Yes, there are tools that can help you. It's still easier to just move your eyes up the screen a bit.
  • Code naturally forms a sort of tree of abstraction: a few general functions at the root, branching out into several implementation details. If you use an editor with function folding/collapsing, nesting creates a hierarchy of closely-related functions at the same level of abstraction. That makes it very easy to study the code at the level you need and hide the details.

I think a lot of opposition comes from the fact that most programmers were either brought up in a C/C++/Java tradition, or were taught by someone else who was. Nested functions don't look natural because we weren't exposed to them much when we were learning to program. That doesn't mean they aren't useful.

14

You should put it at global scope, for several reasons.

  • Nesting a helper function into the caller increases the length of the caller. Function length is almost always a negative indicator; short functions are easier to understand, to memorize, to debug and to maintain.

  • If the helper function has a sensible name, reading that name is enough without needing to see the definition nearby. If you do need to see the helper definition in order to understand the caller function, then that caller is doing too much, or is working on too many levels of abstraction simultaneously.

  • Having the helper globally available allows other functions to call it if it turns it to be generally useful after all. If the helper isn't available, you're tempted to cut and paste it, or to forget it and reimplement it, poorly, or to make another function longer than it has to be.

  • Nesting the helper function increases the temptation to use variables from the caller's scope without declaration, so that it becomes unclear what the inputs and outputs of the helper are. If a function doesn't clearly state what data it operates on and what effects it has, it is usually a sign of unclear responsibilities. Declaring the helper ass a standalone function forces you to know just what it actually does.

Edit

That turned out to be a more controversial question than I thought. To clarify:

In JavaScript, big file-spanning functions often fulfill the role of classes because the language doesn't provide any other scope-limiting mechanism. Certainly helper functions should go inside such quasi-classes, not outside them.

And the point about easier reuse presupposes that if a subroutine does become more widely used, you are willing to move it out of place altogether and put it in appropriate place, e.g. a string utility library or into your global configuration registry. If you don't want to order your code like that, then you might as well nest the subroutine, just like you would do with a normal Method Object in a more "blocky" language.

  • Thanks, all very good points. On a side note, how should I generally order my functions? For instance, at the moment, with small programs I just order them alphabetically. But should I group together helper and caller functions? I guess I should at least break down my functions based on which parts of the application they apply to, for instance? – Gary Aug 6 '14 at 20:11
  • I haven't bothered ordering method definitions in a long time. If you program with any degree of professionalism, you should have an environment that lets you jump to any definition with a few keystrokes anyway. Therefore short methods are useful, but short or even well-ordered class files add little value. (Of course, JavaScript used to require all definitions to be placed above their uses or the result would technically be undefined behaviour - can't recall if that is still the case or not.) – Kilian Foth Aug 6 '14 at 20:30
  • 2
    This is a great answer. The problem of broken encapsulation is something many don't consider when using inner functions. – sbichenko Aug 11 '14 at 11:02
  • 2
    Globals are a code smell. They cause unnecessary coupling which prevents refactoring, they cause name conflicts, they expose implementation details, etc. This is especially bad in Javascript, since all variables are mutable, code is typically loaded directly from third-parties, there isn't (yet) a widely-available module system, or even a widely-available implementation of "let". In such a hostile environment, the only defensive strategy we have is encapsulation inside one-shot functions. – Warbo Aug 12 '14 at 16:10
  • 1
    "Fulfilling the role of classes" is trying to write JS as if it were something else. What is "the role of classes"? Type-checking? Modularity? Staged compilation? Inheritance? The question involves scoping, so I assume you mean the crude scoping rules of classes. That's just passing the buck: how should the classes be scoped? PHP makes them global, which provides no encapsulation. Python scopes classes lexically, but if you're in a lexically-scoped block, why wrap everything in another, class-scoped block? JS doesn't have such redundancy: just use as much lexical scope as you need. – Warbo Aug 13 '14 at 9:28
8

I'm going to propose a third path, to place both functions within a closure. It would look like:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

We create the closure by wrapping the declaration of both functions in a IIFE. The return value of the IIFE is the public function, stored in a variable of the name for the function. The public function can be invoked in exactly the same way as if it were declared as a global function, ie functionA(). Note that the return value is the function, not a call to the function, thus no parens at the end.

By wrapping the two functions up like so, functionB is now completely private, and cannot be accessed outside of the closure, but is visible only to functionA. It is not cluttering the global namespace, and is not cluttering the definition of functionA.

0

Defining function B within function A gives function B access to A's internal variables.

So, a function B defined within a function A gives the impression (or at least the possiblity) that B is using or altering A's local variables. While defining B outside of A makes it clear that B doesn't.

Therefore, for the clarity of the code, I would define B within A only if B needs to access local variables of A. If B has a clear purpose that is independent of A, I would definitely define it outside of A.

-1

You should not nest it, because otherwise the nested function will be re-created every time you call the outer function.

-3

Since function B isn't used anywhere else you might as well just make it an inside funtion within function A because that's the only reason why it exists.

  • 1
    this doesn't seem to add anything substantial over points made and explained in prior 3 answers – gnat Aug 13 '14 at 6:40

protected by gnat Feb 21 '17 at 21:35

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.