Skip directly to content

Only you can prevent anonymous functions!

on September 8, 2011 - 5:49pm

Most of the JavaScript you see on the web is full of anonymous functions. I don't think most of them are necessary. Allow me to demonstrate.

Imagine a PHP developer—let's call her Sunny—who doesn't really write JavaScript, but needs to toggle some text on a page. Sunny goes searching for examples on Google, and finds this jQuery snippet:

Click to hide

Bacon ipsum dolor sit amet venison ham hock pork loin, labore sunt fatback id. Bresaola short ribs ball tip tail, quis ea ribeye. Andouille velit tail, minim shankle non hamburger consectetur jowl biltong. Short ribs shank pancetta, excepteur corned beef ut eiusmod labore kielbasa.

Sunny doesn't really get what's going on with the nested function () {} business, but the code works fine. So she happily sticks it into a <script type="text/javascript"></script>, changes the HTML ids to match her content, and forgets all about it... until the next month, when her boss says, "Hey, remember that great little show/hide feature you wrote? Marketing wants the same thing for every section on the page."

Now Sunny has to figure out how to make that little snippet reusable. "No problem!" she thinks. "I'll just make it into a function that takes a couple of id parameters so it knows what links are toggling which paragraphs." So she gives that a try:

function toggle(link_id, paragraph_id) {
  var $link = $('#' + link_id);
  var $paragraph = $('#' + paragraph_id);
  // Show the paragraph if it's hidden.
  if ($paragraph.is(':hidden')) {
    $paragraph.show();
    $link.text('Click to hide');
  }
  // Hide the paragraph if it's already showing.
  else {
    $paragraph.hide();
    $link.text('Click to show');
  }
}

She tests it out in the Firebug console:
toggle('toggler1', 'paragraph1');
It works great! The link text changes and the paragraph toggles. "Now all I have to do is wire it up to the click event," thinks Sunny. "I'll get paragraph1 working, and then I'll iterate over the rest of them." So off she goes:

$('a#toggler1').click(toggle('toggler1', 'paragraph1'));

If you've written much JavaScript, you know what's going to happen when she clicks on that link: Exactly nothing. Eventually Sunny figures out her problem: The jQuery click() function, which you use to add a click event handler, takes a function argument. You can't pass it toggle('toggler1', 'paragraph1'). You have to pass the toggle function itself. JavaScript treats functions as first-class objects, and you pass them around just like any other object.

"But I can't pass just toggle—that doesn't have enough information! How am I supposed to pass in the parameters I need?" Sunny protests. She Googles around some more, and she finds out about the magic of closures! As it turns out, if you write a JavaScript function inside another function, the inner function can see all the variables in the outer function. "So that's why you see all these function () {} declarations inside other functions! Maybe I should turn toggler back into one of those." So she tries again:

Click to hide

Veggies sunt bona vobis, proinde vos postulo esse magis burdock groundnut salad bell pepper fennel turnip greens cabbage desert raisin caulie squash. Burdock horseradish epazote asparagus broccoli taro avocado kohlrabi wattle seed sweet pepper water spinach arugula bunya nuts grape.

Turns out that works fine. But there are still a couple of problems with it, one of which Sunny knows about, and one of which she has yet to discover:

  1. This is going to quit working in a very odd way when she tries iterating over it to add the other paragraphs.
  2. When Sunny went from having a nice, clean, encapsulated toggle function to an anonymous inline function, her code suddenly became harder to read, harder to document, and harder to maintain.

I won't expound on problem #1; there are several ways to solve it (including wrapping her code in yet another (function(){})(), oh joy!). What I want to deal with here is problem #2:

Anonymous inline functions like the one in the second example are hard to read, hard to document, and hard to maintain.

Often developers like Sunny use them because they don't see any other way to get to the data they need. But there's a perfectly good alternative pattern.

Step 1: Use an object

Just like in other programming languages, objects in JavaScript are a good way to keep data and methods together. Sunny can write her object constructor like this:

Toggler = function (linkId, paragraphId) {
  this.$link = $('#' + linkId);
  this.$paragraph = $('#' + paragraphId);
};

To add methods, you add them to the object's prototype:

Toggler.prototype.toggle = function () {
    if (this.$paragraph.is(':hidden')) {
      this.$paragraph.show();
      this.$link.text('Click to hide');
    }
    else {
      this.$paragraph.hide();
      this.$link.text('Click to show');
    }
  });
};

When Sunny wants a new instance of Toggler, she can instantiate it like this:

var toggler1 = new Toggler('toggle-link-1', 'paragraph-1');

Step 2: Function.prototype.bind (or jQuery.proxy)

So now we have an object, which is fine and dandy, but we still have the same problem Sunny had when she tried passing toggle('toggle-link', 'paragraph'): How do we turn toggler1.toggle into something we can pass into jQuery's click() method? The answer is Function.prototype.bind. It's a new JavaScript feature (introduced in ECMAScript 5) that lets you package up a method on an object instance, together with the instance itself, and turn the whole thing into a first-class function you can use as an event handler. Here's how it works:

var toggler1 = new Toggler('toggle-link-1', 'paragraph-1');
var click_handler = toggler1.toggle.bind(toggler1); // <-- this is the magic invocation!
$('a#toggle-link-1').click(click_handler);

That .bind() call is the key to this whole pattern. It's a method on the built-in Function object's prototype. Its superpower is that it lets Sunny specify what she wants this to equal when her method gets called. The this keyword is always supposed to refer to the object that your function is a method of—just like it does in Java or PHP or any number of other languages—but in JavaScript, it doesn't always work that way. For example, if you create a click handler without using .bind() on it, you'll find that this is a reference to the link that got clicked! That's pretty crazy behavior for anyone familiar with object-oriented programming. Fortunately, by using .bind() on your object methods, you can keep the meaning of this sane.

One thing to be aware of is that older browsers don't have Function.prototype.bind available natively. This includes IE8, FF3.6, and all versions of Safari. So for those browsers, you have to provide your own definition of Function.prototype.bind. Mozilla has a handy polyfill you can use. It won't hurt anything in modern browsers, and it'll make .bind() work correctly in older ones.

Alternatively, if you have jQuery 1.4 or higher available, you can use jQuery.proxy in much the same way:

var toggler1 = new Toggler('toggle-link-1', 'paragraph-1');
var click_handler = $.proxy(toggler1.toggle, toggler1);
$('a#toggle-link-1').click(click_handler);

I don't really recommend jQuery.proxy in most cases. It tries to keep track of your bound functions for you, so you can unbind them later even without a reference to your function object, and that can cause problems if you're creating multiple event handlers (say, one for each toggle link on the page, which is Sunny's use case). I do often use it when writing JavaScript for Drupal, though, since Drupal doesn't have Function.prototype.bind available (yet!).

Putting it together

Here's how Sunny could write her toggler code using objects and Function.prototype.bind:

Click to hide

Skateboard quis et vegan cliche lomo messenger bag, mcsweeney's mollit lo-fi american apparel sint tofu blog. Etsy wolf PBR irony nesciunt, scenester ut mollit freegan cosby sweater mixtape viral vero. Tofu culpa freegan magna, non aute bicycle rights dolor gentrify DIY esse.

Click to hide

Sockeye salmon. Saber-toothed blenny flagfish banded killifish dragonfish goldeye boga fathead sculpin sargassum fish. Unicorn fish sandperch zebra bullhead shark Atlantic saury. Panga goby, “darter tripletail,” barreleye walleye rockweed gunnel southern flounder.

Obviously this isn't the most efficient way to implement a toggle link—there are lots of better ways to do it (jQuery toggle(), for one!). The point here is to illustrate how .bind() lets you write object-oriented JavaScript that respects the this keyword. It's a technique that could eliminate a lot of undocumented anonymous functions if more people knew about it.

I'm especially interested in getting more people in the Drupal community familiar with this pattern, so if you maintain a Drupal module and you want to give this a try, come find me on IRC in #drupal-contribute and I'll be happy to help. I've also proposed a workshop at the Pacific Northwest Drupal Summit where we'll be refactoring JavaScript from the audience. If you're coming to the summit and you have some JavaScript you're not proud of, bring it by for a few small repairs!

Comments

Anonymous coward's picture

If you're doing this in Drupal you need either $(context).find('#id') or $('#id', context) instead of just $('#id') or your JS will run multiple times whenever anything AJAX-y happens.

acouch's picture

What would happen if you didn't use the .bind() function and used "this.$link.click(this);"

Thanks!

Katherine Senzee's picture

You'd end up with the Toggler constructor as your click handler. So every time you clicked on the link, it would run the Toggler constructor (but as a regular function, not as a constructor). Basically it would be useless.

3d printer sales's picture

Hi there colleagues, pleasant article and pleasant arguments commented at
this place, I am really enjoying by these.

regal assets customer reviews's picture

1 place we deseire to touch on can be your heightened
volatility high frequency trading is wearing the current market.

With the help of thee box, you can mail your jewelry to the company for valuation. Considered having food inn an quantity that may fill this worlds bellies and nonetheless not lessen.

ausband law firm in atlanta's picture

It is advisable for discussing your personal injury case with the person who may possess the correct skills and are not amateurs.

The presence of the Convention is a vital element in steering any investigation into whatever incidents may cause the above mentioned losses.
Accordingly, your lawyer claims a compensation that is more tha fifteen times the amount you would have received withouut legal help from the lawyer by settling promptly.

Paul Linney's picture

I would say what you proposed as a solution to demonstrate not using anonymous functions does not really fit your use case. Providing a quick and simple solution using selectors would suffice.

Once that the initial solution does not fit, I would start by tidying up the markup and when working with selectors its just as important as it will have a direct impact on the amount of jquery you will have to write.

Firstly, there is no need to have specific IDs on the A or P tags, an ID for the container is sufficient or use a class if repeated. The elements you need to apply this event to stick a class on them, so toggle-link-1 and toggle-link-2 both get the class 'toggle-link'.

From here you have a minor edit of the original code,
Change the event selector to: "$('a.toggle-link').click(function () {"
And change the paragraph selector to: "var $paragraph = $(this).next('p');"

The code now applies to all the links that need it and toggle the P tag next to them.

With this solution you still have the anonymous function, but its really not that difficult to read: 'Select all A tags with class 'toggle-link' and attach the click event'. This prevents adding additional properties to protoype and you write less code.

There are cases where a structured javascript/jquery does become a requirement and when this is identified, then there are many options to choose from depending on the scope of the application. A resource to get an overview of the options can be found in this online book, http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/.

Paul

Katherine Senzee's picture

I agree this is a very simple use case—much too simple to use in real life. I struggled to find an example simple enough for illustration yet with enough features to be useful. And truth be told, when I see a one-line anonymous function in code review, I usually let it stand. But anything longer than that makes me unhappy. I like the way my colleague Paul Lovvik puts it: "If a function is important enough to write, it's important enough to name."

better search's picture

Wow! In the end I got a weblog from where I know how to actually take useful data regarding my study and knowledge.

ankle braces for foot drop's picture

Befߋre any orthodontist, you may want to compare prices Ьetween thе first number.
If yоu aгe undergoing breast augmentation surgery оr haѵe just done ѕo, ƴоu shoսld draw strength from tɦe knowledge tҺat well oveг a half mіllion women around
the woгld undergo tɦe procedure еach үear.
Beresnyak Αгe mentors who are able to give the type of helρ onlү a Dr.

tstoeckler's picture

str_replace('Sunny', 'tstoeckler', $this_article);Thank you so much for this awesome write-up! :)

Information about England on Wiki's picture

I'm gone to say to my little brother, that hee should also
pay a visit this blog on regular basis to obtain updated from hottest nnews update.

Bryce's picture

Cool things lets you discover and share everything cool.
Be it an excellent new gadget or a hot new car, some thing cool to perform or a cool
destination to visit. We cover each of the cool stuff.
https://www.facebook.com/wwwgopolinocom

Stelios's picture

A comprehensive explanation of a very common problem, thanks a lot! OOP principles can help greatly in making code readable and reusable, though in the easy-going, allow-mostly-anything coding practices of JavaScript, programmers tend to take the quick and dirty approach all too often. Your example is clear, informative, and well presented, showing clear benefits for adopting an OOP approach. Thank you!

featured products &amp; reviews's picture

Plastic surgery has become immensely popular in the last few years.
Calvert states, it requires a great deal of planning and preparation, as well as a lengthy recovery time (a three to five week hospital stay, with about six weeks of home rest prior to
returning to work). Women are more likely to undergo plastic
surgery but some males also undergo this medical procedure if they
think they need to have some major changes in their body.

Feel free to surf to my site - featured products & reviews

best food chain restaurants's picture

Excellent post. Keep writing such kind of info on your site.
Im really impressed by your site.
Hello there, You have performed an excellent job. I'll
certainly digg it and for my part suggest to
my friends. I am confident they will be benefited from this website.

Learn More On The Chinese Wikipedia's picture

I love what you guys tend to be up too. Such clevber work and coverage!
Keep up the superb works guys I've included you guuys to blogroll.

Sidharth Kshatriya's picture

Nice article on Function.prototype.bind() and avoiding anonymous functions! Thanks!

Vikky's picture

Too bad it doesnt work in Safari. Hence wont work on iOS devices

Katherine Senzee's picture

Safari is falling a bit behind on this (https://bugs.webkit.org/show_bug.cgi?id=26382). But if you use the polyfill snippet I linked in the article, it'll work fine. You also need the polyfill for IE8 and below, plus older versions of Firefox. I wouldn't use this technique without the polyfill on anything but an experimental site.

Josh Caldwell's picture

I believe that you're confusing 2 different issues in the blog post while pointing to anonymous functions as the enemy. I don't see any evidence that anonymous functions create less readable or maintainable code - actually, I would argue that your OO solution to the problem is much harder to understand. For example, you could have solved the original problem by using an anonymous function like so:  http://pastie.org/2522430 (wysiwyg does not like my code block formatting) which would have been more concise and arguably more readable. I believe that the functional aspects that javascript presents can lead to really beautiful solutions and simply banishing anonymous functions as 'unmaintainable' is a bad path to go down.That said, I agree that binding functions to objects is a very important concept to grasp, and having a nice crossbrowser bind function is really useful.

spray tanning kits at home's picture

For those of us in temperate climates, this marks the time when we sadly say goodbye
to summer shorts and shirtless tank tops, and trade them in for heavier,
warmer wardrobe. Other than spray tan, Sun Laboratories also offers other beauty and
skin care products including body polisher, sun tan lotion, soothing and sensual
hand and body wash, cream and lotion, tan maintainer, tan overnight lotion, sunscreen,
dark sunsation air brush, aloevera gel, ice finalizing gel,
sunscreen glitter and lip balm. However, there may not be perfect
attempts in beginning but after two or three
applications; you'll learn how to do it flawlessly. Airbrush spray cans
are used by professionals using specialized equipment such as LVLP spray gun and HVLP spray gun equipment.
After I shave my legs I always put moisturizer on them and they sometimes tingle depending on the brand of moisturizer I use.

Spray tans are available in light to dark shades that complement every skin tone.
It produces outstanding sunless tanning with minimal effort.

Also ensure that you don't go for a shower right after the tanning session as
it will wash away the tanning solution before it gets time to set in. This prevents blotchy colour that results from dead skin cells in these areas.

You can control and customize the level of your tan.

Here is my web-site spray tanning kits at home

dascor plumbing pompano beach florida's picture

To check if the flush valve is the cause of the leak, simply shut
off the water supply to the toilet. Keeping your home plumbing running smoothly is not
a difficult job. When you pull the toilet handle, the flapper rises and allows the water in the
tank to empty into the toilet.

My webpage dascor plumbing pompano beach florida

Bored's picture

uhh... couldn't you like... not use jquery and then use a single global function with

'a href=javascript:toggle(elementid)' or 'a onclick=toggle' (sending event origin).

You don't even need to wait for the document to be ready and just instantly do what ever you want to do. After all, using a constructor for something as tiny as this seems like you're only creating memory bloat --- something that a named function would cure.

* Memory may be "free" but try telling that to someone with 100+ tabs open. If Sunny just learned a few things about DOM and html properties, she might even be able to put the whole thing in a div and grab the paragraph element without knowing the ID of either the anchor, the paragraph, or the div! (I'm not really convinced that she even needs the div to do that).

Really, Sunny should stop relying on jQuery, given she is a PHP specialist; and take a look at the far stronger framework that jQuery macros off of. After all, if Sunny's job is just to copy and paste jQuery snippets from Google... she might be replaced with a high school drop out (who can do the same exact thing).

Joseph Brown's picture

Nice article.... give this to Sunny

// please stop creating anonymous variables and functions
var toggler1 = undefined;
var toggler2 = undefined;

// stop being slaves to subpar browsers and force consistency (refuse to use attachEvent)
// wouldn't it be nice if we had event constants? document.addEventListener(document.LOAD_COMPLETE, init);
document.addEventListener("DOMContentLoaded", init);

function init()
{
// remove your event listeners when you're done with them please!
document.removeEventListener("DOMContentLoaded", init);

// do these actually need to be global?
// choose, but choose wisely
toggler1 = new ViewToggler("link1", "paragraph1");
toggler2 = new ViewToggler("link2", "paragraph2");

toggler1.listParams();
toggler2.listParams();
}

function ViewToggler(linkId, paragraphId)
{
// private properties
var link = document.getElementById(linkId);
var paragraph = document.getElementById(paragraphId);

// constructor
link.addEventListener("click", hide);

// public methods
this.listParams = function()
{
console.log("link: " + link.id);
console.log("paragraph: " + paragraph.id);
}

// private methods
function hide(event)
{
link.removeEventListener("click", hide);
link.addEventListener("click", show);
link.innerHTML = "Click to show";
paragraph.style.display = "none";
}

function show(event)
{
link.addEventListener("click", hide);
link.removeEventListener("click", show);
link.innerHTML = "Click to hide";
paragraph.style.display = "block";
}
}

jasa adwords jogja's picture

Relevant ads tend to earn more clicks, appear in a higher position, and bring
you the most success. In most cases, advertisers don't pay
anything unless their ad appears and someone clicks it.
I have one ad in my side bar and one at the bottom of each article.

Chris Warburton's picture

I think you're confusing a few distinct issues here, which deserve to be teased
apart: first-class functions, anonymous values and higher-order functions.

Having first-class functions means that functions are values. Other examples of
values are numbers (1, -10, 0xF, 1e5, etc.) and strings ("hello", 'world', etc.)
First-class functions are a Good Thing, since they allow for more abstraction,
composition, encapsulation and code-reuse.

Having anonymous values means writing values in literal form wherever they're
used. We can see exactly *what* they are, but we don't know *why* they are.
See http://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants

Some examples of anonymous values:

alert('Hello'); // An anonymous string
hide(537); // An anonymous number
setTimeout(function() { giveUp(foo); }, timeout); // An anonymous function

Anonymous values improve locality and *can* make code much more readable. For
example, the variable 'multiplier' is completely unnecessary here:

var double = function(x) {
var multiplier = 2;
return x * multiplier;
}

They shouldn't be used for everything though, because they can also make code
less readable. Consider the 'hide(537)' line above; what is the significance
of 537? It should probably be given a name:

var imageIndex = 537;
hide(imageIndex);

Anonymous values are also inappropriate when we need to use the same semantic
value multiple times. I say 'semantic value' since, for example, a user may be
12 years old and today may be the 12th October. They are the same literal value
(12) but they have a different meaning, so should have different names. For
example:

alert((age < 26)? 'Wait ' + (26 - age) + ' years'
: 'Welcome');

Here the anonymous value 26 is reused; we may be better off with this:

var minAge = 26;
alert((age < minAge)? 'Wait ' + (minAge - age) + ' years'
: 'Welcome');

Anonymous functions are just a special case of anonymous values, but one which
some (very small number of) languages don't support because they don't have
first-class functions (or equivalent, like function pointers). Each case should
be judged on its merits.

Higher-order functions are the key to the whole thing though. With first-class
functions (and lexical scope) we can implement more useful control flow. For
example, map is a higher-order function since it takes a function as an
argument. We can't define map in a language without first-class functions.
Likewise for fold ('reduce'), LISP's 'cond', etc. The real power comes when we
define task-specific control flow; for example it's quite simple to make a JS-
powered spreadsheet if you wrap all of the values in thunks and memoise the
results. This is trivial with higher-order functions, but a nightmare without.

I've written various examples of higher-order functions, in Javascript and other
languages, which can be found on my blog:

http://chriswarbo.net/index.php?page=news&type=view&id=admin-s-blog%2Fbe...
http://chriswarbo.net/index.php?page=news&type=view&id=currying-in-javas...
http://chriswarbo.net/index.php?page=news&type=view&id=admin-s-blog%2F-a...
http://chriswarbo.net/index.php?page=news&type=view&id=admin-s-blog%2Fpe...
http://chriswarbo.net/index.php?page=news&type=view&id=search-and-optimi...
http://chriswarbo.net/index.php?page=news&type=view&id=closures-vs-first_2

PS: As for the title, if all of our functions are first-order then any hierarchical structure in our code has been collapsed, leaving us with a verbose, tangled mess.

Kollen's picture

Scientists reviewed seven prospective studies involving more than 2,50,000 people and found that after adjusting for various stroke risks and for other nutrients consumed, those who had the highest consumption of protein had a 20 per cent decreased risk for stroke compared with those with the lowest.

Frederico Miranda's picture

Wonderful Text. Exactly what I was looking for. I'm getting used to strict Javascript, which makes not possible to to nest functions.

weight loss pills for kids's picture

Screwing it on the crown will be the sеcond step that ԝe will execute.
There aге a lot оf people that struggle understanding ɦow theʏ are ցoing to Ƅe wise aƄоut tҺe աay they gеt ready foг
plastic surgery. Tɦе scar tissue, Һowever, ѕometimes Ƅecomes abnormally tɦick,
whіch can lead to pain, inflammation, and restriction ߋf thе arm.

สมัครเรียนต่อ's picture

According to the latest government regulation, all the faculty members need to have a degree in the discipline that they are
imparting. Your shoulders should be covered-this means no tank tops.
Distance education is an effective way of implementing education to students living in distant remote areas.

Feel free to surf to my website ... สมัครเรียนต่อ

Kollen's picture

Thanks so much for writing all these informations. Marion Hodinky

Kollen's picture

Ja chovám králiky plemena Nemecký obrovitý strakáč už niekoľko rokov. Pre mňa je chov králikov záľuba, ktorá mi dúfam vydrží až do dôchodcovského veku.

Norfolk accident injury attorney's picture

Hmm it looks like your site ate my first comment (it was super long)
so I guess I'll just sum it up what I had written and
say, I'm thoroughly enjoying your blog. I as well am
an aspiring blog writer but I'm still new to everything. Do you have any
helpful hints for beginner blog writers? I'd certainly appreciate it.

Anamaria's picture

Dosť dlho som zháňala také kabelky, ktoré by sa mi páčili, boli lacné a trendové. A práve také som konečne našla na aaz.sk.

Deep's picture

Relevant ads tend to earn more clicks, appear in a higher position, and bring you the most success. In most cases, advertisers don't pay anything unless their ad appears and someone clicks it. I have one ad in my side bar and one at the bottom of each article. Please take out time to read my article iPhone 6 Price in india

haronsa's picture

A good informative post that you have shared and thankful your work for sharing the information.Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic.
http://www.gamereasy.com/Buy-Gold-The-Elder-Scrolls-Online.html

Post new comment