What are the benefits to using anonymous functions instead of named functions for callbacks and parameters in JavaScript event code?
Asked Answered
B

6

82

I'm new-ish to JavaScript. I understand many of the concepts of the language, I've been reading up on the prototype inheritance model, and I'm whetting my whistle with more and more interactive front-end stuff. It's an interesting language, but I'm always a bit turned off by the callback spaghetti that is typical of many non-trivial interaction models.

Something that has always seemed strange to me is that in spite of the readability nightmare that is a nest of JavaScript nested callbacks, the one thing that I very rarely see in many examples and tutorials is the use of predefined named functions as callback arguments. I'm a Java programmer by day, and discarding the stereotypical jabs about Enterprise-y names for units of code one of the things I've come to enjoy about working in a language with a strong selection of featureful IDE's is that using meaningful, if long, names can make the intent and meaning of code much clearer without making it more difficult to actually be productive. So why not use the same approach when writing JavaScript code?

Giving it thought, I can come up with arguments that are both for and against this idea, but my naivety and newness to the language impairs me from reaching any conclusions as to why this would be good at a technical level.

Pros:

  • Flexibility. An asynchronous function with a callback parameter could be reached by one of many different code paths and it could be harried to have to write a named function to account for every single possible edge case.
  • Speed. It plays heavily in to the hacker mentality. Bolt things on to it until it works.
  • Everyone else is doing it
  • Smaller file sizes, even if trivially so, but every bit counts on the web.
  • Simpler AST? I would assume that anonymous functions are generated at runtime and so the JIT won't muck about with mapping the name to instructions, but I'm just guessing at this point.
  • Quicker dispatching? Not sure about this one either. Guessing again.

Cons:

  • It's hideous and unreadable
  • It adds to the confusion when you're nested nuts deep in a swamp of callbacks (which, to be fair, probably means you're writing poorly constructed code to begin with, but it's quite common).
  • For someone without a functional background it can be a bizarre concept to grok

With so many modern browsers showing the ability to execute JavaScript code much faster than before, I'm failing to see how any trivial sort of performance gain one might get out using anonymous callbacks would be a necessity. It seems that, if you are in a situation where using a named function is feasible (predictable behavior and path of execution) then there would be no reason not to.

So are there any technical reasons or gotchas that I'm not aware of that makes this practice so commonplace for a reason?

Bantu answered 22/4, 2012 at 23:49 Comment(10)
I don't agree with most of your "pros", sounds like you're just guessing. Not fond of your cons, either. Bottom line is that for short functions, it just makes more sense. Just like in Java, we don't create a named class for short, contained purposes, we use an anonymous inner class. JS uses any if a variety of module patterns when complexity warrants passing re-usable and/or non-trivial functions. This is used all over; not having seen it doesn't mean it's not used when it makes sense to do so.Electronarcosis
@DaveNewton I know that it's out there, I'm just asking why it doesn't seem to be the standard, and if there are any objective, technical limitations as to why this is the case. And as for anonymous inner classes in Java, I only tend to use those when I have no need to do a concrete implementation of an interface, like if I need to spawn a very simple Runnable. And when I do it, I dont' like it.Bantu
In IE (at least some versions), using named function expressions results in two different function instances (Note that var a = function(){...} is not a named function (expression). The function assigned to a is still anonymous). That's why it's better to avoid them. Of course you can always use a function declaration instead, but it seems unnecessary to create a symbol in the scope if it is only used once.Bushey
@FelixKling that seems like a good, objective, technical reason to avoid them. But what about function statements/declarations?Bantu
@FelixKling does IE have any hiccups when they are named in that context as well?Bantu
Function declarations are always named, since they are of the form function name(){...} (that's the complete statement, just having function(){...} is invalid) and no, there are no problems afaik.Bushey
"Bolt things on to it until it works" is not a hacker mentality, that's an "I don't understand how my code works, it is all magic to me" mentality.Radiophotograph
@FelixKling I know they're always named, I just wondered if there was a technical issue with using that as a naming mechanism since you had given me a good reason to avoid function expressions.Bantu
@DougStephen Well, to me it sounds like you have an aversion to anonymous functions/classes in general, without really providing a technical reason for it. They're simply the right tool for particular jobs, and doing some types of Java programming without them would be... ridiculous.Electronarcosis
@DaveNewton They have their place, I'm not disagreeing with that. But there are definitely places that they CAN be used but aren't needed/aren't the best fit. Yet it seems to be that using anonymous functions is a knee jerk in JS. And you're right, I do have a bit of an aversion to them. And not for a technical reason, but because I don't feel that they are clear (in some contexts). Closures and anonymous functions are very powerful, but I'm trying to approach this from a human-readable/right-tool-for-the-job context.Bantu
R
68

I use anonymous functions for three reasons:

  1. If no name is needed because the function is only ever called in one place, then why add a name to whatever namespace you're in.
  2. Anonymous functions are declared inline and inline functions have advantages in that they can access variables in the parent scopes. Yes, you can put a name on an anonymous function, but that's usually pointless if it's declared inline. So inline has a significant advantage and if you're doing inline, there's little reason to put a name on it.
  3. The code seems more self-contained and readable when handlers are defined right inside the code that's calling them. You can read the code in almost sequential fashion rather than having to go find the function with that name.

I do try to avoid deep nesting of anonymous functions because that can be hairy to understand and read. Usually when that happens, there's a better way to structure the code (sometimes with a loop, sometimes with a data table, etc...) and named functions isn't usually the solution there either.

I guess I'd add that if a callback starts to get more than about 15-20 lines long and it doesn't need direct access to variables in the parent scope, I would be tempted to give it a name and break it out into it's own named function declared elsewhere. There is definitely a readability point here where a non-trivial function that gets long is just more maintainable if it's put in its own named unit. But, most callbacks I end up with are not that long and I find it more readable to keep them inline.

Rafa answered 22/4, 2012 at 23:58 Comment(14)
1. This argument seems like it could be extended out to encompass everything we give a name to. 2. This argument seems a little circular, but I think I see what you're saying. 3. The goal would be to give the functions extremely clear, meaningful names so that you don't have to go find them.Bantu
@DougStephen - On point 1, I'm just saying that when a callback is declared inline, giving it a name doesn't really provide you with any additional information. You know exactly what it's for from the context in which it is seen and declared. Perhaps this is a learned interpretation that you get used to after seeing the construct a lot, but now when I see it, it looks no different to me than a for loop and I don't think a for loop needs to be broken out and given a name to be readable.Rafa
I see what you're saying. And this probably fits in with the idea that naming a function is only appropriate in certain contexts (predictable behavior). In which case you wouldn't be creating the function inline, and so you could name it. Obviously naming the function inline would be silly.Bantu
@DougStephen - also, don't underestimate the advantage of point 2. Some types of code can be written tons simpler by having access to parent scoped variables rather than having to put everything into objects and pass them around as arguments. The most common place this is handy is when a callback wants access to the this pointer from the original function. You can store that in a local variable of the parent function (commonly done as var self = this; and then you can access self in the anonymous function. Very, very handy.Rafa
I see where it is handy, and I won't disagree with you. But it also seems a little side-effecty. It might be a "hassle" to send information like that down as a parameter but it also seems more controlled to me. I'm also not a big fan of accessing outer scoped variables even when working in languages like C.Bantu
@DougStephen - I think you're just newish to Javascript (as you say) and haven't really come to appreciate the benefits of closures and access to parent scopes. There are lots of interfaces in JS where they don't make it easy for you to pass arbitrary things through to callbacksfrom parent functions (because they don't need to) so using an anonymous function and accessing them via the parent scope is the natural way to write the code. It is different than C/C++. It will feel more "natural" and "right" over time.Rafa
I'm aware of the benefits provided by closures, and I'm not arguing against that. But if you don't need access to that sort of functionality, why not try to modularize and "prettify" your code base a little bit? That's what I'm trying to get at. Objective limitations that make doing so a bad idea.Bantu
@DougStephen - "Prettify" is in the eye of the beholder and I think a seasoned Javascript developer has a different view of what's pretty than a seasoned C/C++ developer. If you spend a lot of time writing JS and studying JS code written by very experienced folks, I think your viewpoint will change over time as to what is ordered, readable, most maintainable and pretty. Mine did. Obviously I can't convince you of anything here, just alert you to the other viewpoints and let you know that it's common to change your ideas over time as you really learn JS when coming from C/C++.Rafa
I'll concede to you on that one.Bantu
Isn't there something about naming functions being a good idea because it makes stack traces more legible?!?Mightily
I would also mention the defer and async js frameworks which allow you to create asynchronous or synchronous calls where the syntax can still use anonymous functions but in a single-nested fashion declared like a list of functions.Asphyxiate
@zipstory.com - how is your suggestion different than my points #2 and #3?Rafa
Its a related topic in how to solve for deep nesting in JavaScript by leveraging existing frameworks. I'm not understanding your question.Asphyxiate
BTW, naming an anonymous functions has its own advantages and the BIGGEST one is when you see something failing on stacktrace, you will see a name of the function and not "anonymous". It also means reader doesnt have to look back at the API to figure out which argument is success and which one is failure, self documenting!Daliladalis
J
11

I prefer named functions myself, but for me it comes down to one question:

Will I use this function anywhere else?

If the answer is yes, I name/define it. If not, pass it as an anonymous function.

If you only use it once, it doesn't make sense to crowd the global namespace with it. In today's complex front-ends, the number of named functions that could have been anonymous grows quickly (easily over 1000 on really intricate designs), resulting in (relatively) large performance gains by preferring anonymous functions.

However, code maintainability is also extremely important. Each situation is different. If you're not writing a lot of these functions to begin with, there's no harm in doing it either way. It's really up to your preference.

Another note about names. Getting in the habit of defining long names will really hurt your file size. Take the following example.

Assume both of these functions do the same thing:

function addTimes(time1, time2)
{
    // return time1 + time2;
}

function addTwoTimesIn24HourFormat(time1, time2)
{
    // return time1 + time2;
}

The second tells you exactly what it does in the name. The first is more ambiguous. However, there are 17 characters of difference in the name. Say the function is called 8 times throughout the code, that's 153 extra bytes your code didn't need to have. Not colossal, but if it's a habit, extrapolating that to 10s or even 100s of functions will easily mean a few KB of difference in the download.

Again however, maintainability needs to be weighed against the benefits of performance. This is the pain of dealing with a scripted language.

Jarret answered 23/4, 2012 at 14:55 Comment(3)
If you are minimizing your javascript for deployment, the point about the size of the names is no longer relevant. Opt for readability in this case.Hickory
Only if your functions/variables are defined inside of closures (which they should be). Anything that looks like it could be global to the minifyer will be left alone, and the length of the name still matters.Jarret
I'll add to this.. People that use anonymous functions are not thinking of future development or support of the product. A function is meant to be reused and something you write today, just because you only use it once, doesn't mean someone else will not need it later. I find anonymous functions are for lazy developer and I refuse to use them.Interspace
N
11

A bit late to the party, but some not yet mentioned aspects to functions, anonymous or otherwise...

Anon funcs are not easily referred to in humanoid conversations about code, amongst a team. E.g., "Joe, could you explain what the algorithm does, within that function. ... Which one? The 17th anonymous function within the fooApp function. ... No, not that one! The 17th one!"

Anon funcs are anonymous to the debugger as well. (duh!) Therefore, the debugger stack trace will generally just show a question mark or similar, making it less useful when you have set multiple breakpoints. You hit the breakpoint, but find yourself scrolling the debug window up/down to figure out where the hell you are in your program, because hey, question mark function just doesn't do it!

Concerns about polluting the global namespace are valid, but easily remedied by naming your functions as nodes within your own root object, like "myFooApp.happyFunc = function ( ... ) { ... }; ".

Functions that are available in the global namespace, or as nodes in your root object like above, can be invoked from the debugger directly, during development and debug. E.g., at the console command line, do "myFooApp.happyFunc(42)". This is an extremely powerful ability that does not exist (natively) in compiled programming languages. Try that with an anon func.

Anon funcs can be made more readable by assigning them to a var, and then passing the var as the callback (instead of inlining). E.g.: var funky = function ( ... ) { ... }; jQuery('#otis').click(funky);

Using the above approach, you could potentially group several anon funcs at the top of the parental func, then below that, the meat of sequential statements becomes much tighter grouped, and easier to read.

Nakesha answered 20/11, 2016 at 16:54 Comment(0)
T
4

Anonymous functions are useful because they help you control which functions are exposed.

More Detail: If there is no name, you can't reassign it or tamper with it anywhere but the exact place it was created. A good rule of thumb is, if you don't need to re-use this function anywhere, it's a good idea to consider if an anonymous function would be better to prevent getting tampered with anywhere.

Example: If you're working on a big project with a lot of people, what if you have a function inside of a bigger function and you name it something? That means anyone working with you and also editing code in the bigger function can do stuff to that smaller function at any time. What if you named the function "add" for instance, and someone reassigned the label "add" from a function to a number instead? Then the whole thing breaks!

PS -I know this is a very old post, but there is a much simpler answer to this question and I wish someone had put it this way when I was looking for the answer myself as a beginner- I hope you're ok with reviving an old thread!

Topdrawer answered 9/6, 2021 at 18:59 Comment(3)
Most of what you're suggesting can be accomplished by let and const scopes. There aren't many good reasons to use IIFEs in 2021. See: levelup.gitconnected.com/…Lightfoot
Hey Chris! Thanks for your feedback too. Anonymous functions aren't the same as IIFE's,, but the article was a great one to share. Anonymous functions are still good to know how they work and what they do for a beginner learning the fundamentals, though. Especially if they haven't learned about modules yet.Topdrawer
Thanks for pointing this out Lindsay -- you are of course correct. Agreed that these are important concepts worth learning. For the sake of completeness here is another article that goes over the differences and similarities between the two concepts: medium.com/@DaphneWatson/…Lightfoot
A
3

Its more readable using named functions and they are also capable of self-referencing as in the example below.

(function recursion(iteration){
    if (iteration > 0) {
      console.log(iteration);
      recursion(--iteration);
    } else {
      console.log('done');
    }
})(20);

console.log('recursion defined? ' + (typeof recursion === 'function'));

http://jsfiddle.net/Yq2WD/

This is nice when you want to have an immediately invoked function that references itself but does not add to the global namespace. It's still readable but not polluting. Have your cake and eat it to.

Hi, my name is Jason OR hi, my name is ???? you pick.

Asphyxiate answered 11/2, 2014 at 16:19 Comment(0)
L
1

Well, just to be clear for the sake of my arguments, the following are all anonymous functions/function expressions in my book:

var x = function(){ alert('hi'); },

indexOfHandyMethods = {
   hi: function(){ alert('hi'); },
   high: function(){
       buyPotatoChips();
       playBobMarley();
   }
};

someObject.someEventListenerHandlerAssigner( function(e){
    if(e.doIt === true){ doStuff(e.someId); }
} );

(function namedButAnon(){ alert('name visible internally only'); })()

Pros:

  • It can reduce a bit of cruft, particularly in recursive functions (where you could (should actually since arguments.callee is deprecated) still use a named reference per the last example internally), and makes it clear the function only ever fires in this one place.

  • Code legibility win: in the example of the object literal with anon funcs assigned as methods, it would be silly to add more places to hunt and peck for logic in your code when the whole point of that object literal is to plop some related functionality in the same conveniently referenced spot. When declaring public methods in a constructor, however, I do tend to define labeled functions inline and then assign as references of this.sameFuncName. It lets me use the same methods internally without the 'this.' cruft and makes order of definition a non-concern when they call each other.

  • Useful for avoiding needless global namespace pollution - internal namespaces, however, shouldn't ever be that broadly filled or handled by multiple teams simultaneously so that argument seems a bit silly to me.

  • I agree with the inline callbacks when setting short event handlers. It's silly to have to hunt for a 1-5 line function, especially since with JS and function hoisting, the definitions could end up anywhere, not even within the same file. This could happen by accident without breaking anything and no, you don't always have control of that stuff. Events always result in a callback function being fired. There's no reason to add more links to the chain of names you need to scan through just to reverse engineer simple event-handlers in a large codebase and the stack trace concern can be addressed by abstracting event triggers themselves into methods that log useful info when debug mode is on and fire the triggers. I'm actually starting to build entire interfaces this way.

  • Useful when you WANT the order of function definition to matter. Sometimes you want to be certain a default function is what you think it is until a certain point in the code where it's okay to redefine it. Or you want breakage to be more obvious when dependencies get shuffled.

Cons:

  • Anon functions can't take advantage of function hoisting. This is a major difference. I tend to take heavy advantage of hoisting to define my own explicitly named funcs and object constructors towards the bottom and get to the object definition and main-loop type stuff right up at the top. I find it makes the code easier to read when you name your vars well and get a broad view of what's going on before ctrl-Fing for details only when they matter to you. Hoisting can also be a huge benefit in heavily event-driven interfaces where imposing a strict order of what's available when can bite you in the butt. Hoisting has its own caveats (like circular reference potential) but it is a very useful tool for organizing and making code legible when used right.

  • Legibility/Debug. Absolutely they get used way too heavily at times and it can make debug and code legibility a hassle. Codebases that rely heavily on JQ, for instance, can be a serious PITA to read and debug if you don't encapsulate the near-inevitable anon-heavy and massively overloaded args of the $ soup in a sensible way. JQuery's hover method for instance, is a classic example of over-use of anon funcs when you drop two anon funcs into it, since it's easy for a first-timer to assume it's a standard event listener assignment method rather than one method overloaded to assign handlers for one or two events. $(this).hover(onMouseOver, onMouseOut) is a lot more clear than two anon funcs.

Leopoldine answered 2/5, 2012 at 22:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.