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.

livraison fleurs pas cher's picture

I do not even know how I ended up here, buut I thought this post was great.
I don't know who you are but definitely you're going tto a famous
blogger if you are not already ;) Cheers!

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."

Vance's picture

hi!,I like yoour writing very a lot! share we kerp in touch extrta about yor article on AOL?
I require an expert on this area to unrvel myy problem. Maybe
that's you! Takming a ook ahead to see you.

tstoeckler's picture

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

Second Life mod's picture

I hafe гeаd ѕo many articles or reviews regarding the blogger lovers however
this paragraph is genuіnely a fastidious pοst, keep it up.

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!

www.bmag.co.uk's picture

Instead of using butter, you can try margarine, but you need to make sure that the product does not contain
any hidden dairy and animal products like whey protein.

The drawing power of a cooking community that centers on
Filipino food and branches outward is considerable.
If you are heading out on Friday evening and coming back Sunday night, you.

skin's picture

My spouse and I absolutely love your blog and find the majority of your post's to be just what I'm looking
for. Do you offer guest writers to write content to
suit your needs? I wouldn't mind producing a post or elaborating
on a lot of the subjects you write regarding here.

Again, awesome weblog!

code promo florajet's picture

I like reading a post that will make men and women think.

Also, thank you for allowing me to comment!

Sidharth Kshatriya's picture

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

GTA 5 Cheats's picture

We absolutely love your blog and find nearly all of your post's
to be just what I'm looking for. Does one offer guest writers to write content in your case?
I wouldn't mind composing a post or elaborating on
most of the subjects you write regarding here. Again, awesome
web log!

travian cheats's picture

Because the admin of this web site is working, no hesitation very shortly it will be well-known,
due to its quality contents.

hotel venise's picture

When someone writes an post he/she maintains the image of a user in his/her mind that how a user can be aware off it.
Thus that's why this article is outstdanding.
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.

alamat PT's picture

Hello all, here every person is sharing these kinds of experience, so it's
pleasant to read this web site, and I used to visit this weblog
everyday.

Reksadana Syariah's picture

What's up to every body, it's my first pay a quick visit of
this website; this blog contains awesome and genuinely excellent information in favor of readers.

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.

menopause wanita's picture

I know this web site presents quality depending posts and other data, is
there any other website which presents these things in quality?

hotel ciputra semarang's picture

This is a topic that is near to my heart... Take care!
Exactly where are your contact details though?

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).

2015 cadillac srx's picture

I enjoy what you guys tend to be up too. Such clever work
and exposure! Keep up the wonderful works guys I've incorporated you guys to my
blogroll.

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";
}
}

tips cara diet sehat's picture

This info is worth everyone's attention. How can I find out more?

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.

www.Frive.pw's picture

GТ5 is the best release so far, I
s&X69;&X6D;ply love this ga&X6d;e, b&X72;illiantly code&X64; Rockstar!!!

oil's picture

My programmer is trying to convince me to move to
.net from PHP. I have always disliked the idea because of the costs.
But he's tryiong none the less. I've been using Movable-type on several websites for about
a year and am concerned about switching to another platform.
I have heard excellent things about blogengine.net. Is there
a way I can import all my wordpress content into it?
Any help would be greatly appreciated!

Estela's picture

Oh my goodness! Awesome article dude! Many thanks, However I am having troubles with your RSS.
I don't know why I cannot subscribe to it. Is there anyone else getting the same RSS issues?

Anybody who knows the answer will you kindly respond?
Thanx!!

hotel venise centre's picture

Hi there! This post could not be written much better!
Going through this post reminds me of my previous roommate!
He constantly kept talking aboujt this. I'll forward tthis post to him.
Pretty sure he's going to have a very good read. Thhank you for sharing!

Post new comment