Your anonymous functions are bad, and maybe we can fix that.

Using anonymous functions for certain callbacks in JS is an age old practice, but it’s high time we all started doing things a little different

If you’ve worked in javascript more than a handful of times, you’ve probably seen something like this:

[javascript]
$elements.each(function (i, el) {
var $el = $(el);
$el.removeClass(‘mod-no-pointer’).addClass(‘mod-label’);
$el.append($(‘<span class="icon icon-standard img-replace"></span>’));
});
[/javascript]

This is technically valid code, and has been a common practice for some time. the $.each() method EXPECTS a function, so how else are we to do this? It gets even more complicated when we see something like this:

[javascript]
var ParentObject = {

/**
* @method init
* Initializes the parent object
*/
init: function () {
var self = this;

EventBus.subscribe(‘event-name’, function (e) {
$(‘.sidebar-heading’).removeAttr(‘style’);
self.doSomething();
});
},

/**
* @method doSomething
* Does that thing we want it to do.
*/
doSomething: function() {
// does something
}
}
[/javascript]

In this example, we’ve had to create a secondary variable to store the value of “this” because we all know that anonymous functions have their own scope (meaning that inside of that function, “this” refers to the anonymous function, NOT to the parent object).

Whereas I’m certainly guilty of writing my javascript like this in days past, I’ve grown, and learned a few things along the way. What we can learn from this code is that there are two issues: 1) anonymous functions, and 2) maintaining scope within your functions. Both of these can be resolved with Function.prototype.bind().

Using .bind() at the end of a function reference or a function declaration allows us to dictate what “this” means within the scope of that function. In this example, we pass in the “this” of the ParentObject so that the function knows that “this” means the ParentObject

[javascript]
var ParentObject = {

/**
* @method init
* Initializes the parent object
*/
init: function () {
EventBus.subscribe(‘event-name’, function (e) {
$(‘.sidebar-heading’).removeAttr(‘style’);
this.doSomething();
}.bind(this));
},

/**
* @method doSomething
* Does that thing we want it to do.
*/
doSomething: function() {
// does something
}
}
[/javascript]

Now, we still have the issue of an anonymous function. What can we do about that? We can give it a name:

[javascript]
var ParentObject = {

/**
* @method init
* Initializes the parent object
*/
init: function () {
EventBus.subscribe(‘event-name’, this.onEvent.bind(this));
},

/**
* @method doSomething
* Does that thing we want it to do.
*/
doSomething: function() {
// does something
},

/**
* @method onEvent
* Callback to do things when our event happens
*/
onEvent: function (e) {
$(‘.sidebar-heading’).removeAttr(‘style’);
this.doSomething();
}
}
[/javascript]

Notice how we’re not calling bind(this) in the function declaration anymore, but rather we’re calling it on the function reference (this.onEventName). Calling bind() doesn’t actually call the function at that time, it simply creates a copy of the given function, with the new scope. This code is fine for now (and certainly easier to read and work with), but what if we wanted to bind the same method again and again? Each time we call function.bind() we create an entirely new function which uses up more resources, but what if we wanted to only make one copy?

[javascript]
var ParentObject = {

/**
* @method init
* Initializes the parent object
*/
init: function () {
this.onEventHandler = this.onEvent.bind(this);

EventBus.subscribe(‘event-name’, this.onEventHandler);
EventBus.subscribe(‘event-name2’, this.onEventHandler);
EventBus.subscribe(‘event-name3’, this.onEventHandler);
EventBus.subscribe(‘event-name4’, this.onEventHandler);
},

/**
* @method doSomething
* Does that thing we want it to do.
*/
doSomething: function() {
// does something
},

/**
* @method onEvent
* Callback to do things when our event happens
*/
onEvent: function (e) {
$(‘.sidebar-heading’).removeAttr(‘style’);
this.doSomething();
}
}
[/javascript]

In this example, we’ve stored a semi-permanent reference to ParentObject.onEvent, which also maintains the proper scope, in a property called .onEventHandler. From here on out (unless something changes) onEventHandler is a stored function on which we’ve already applied .bind(this). If each of those event subscriptions had this.onEvent.bind(this), then we’d have four different copies of the same code. If our code referenced this 1,000 times, we’d have 1,000 copies of the same code, which would quickly get out of hand.

You might be wondering why we would go through all this trouble for what seems like a rather simple anonymous function that really isn’t causing any problems. In this case, it might not be, but it *could*, especially when working in larger applications. The potential for memory leaks is unbelievable in a large application. Training yourself to do things right, or better, the first time is invaluable to your applications. Not only that, this practice can greatly cut down on the depth of enclosures you have. For example:

[javascript]
var ParentObject = {

/**
* @method init
* Initializes the parent object
*/
init: function () {
var self = this;
EventBus.subscribe(‘event-name’, function (e) {
$(‘.sidebar-heading’).removeAttr(‘style’);
if($(‘.js-items’).length) {
$(‘.js-items’).each(function(i, el) {
var $element = $(el);
$element.on(‘click’, function(e) {
alert(‘clicked’);
});
});
}
});
},
}
[/javascript]

This would be far more maintainable with bound, named functions:

[javascript]
var ParentObject = {

/**
* @method init
* Initializes the parent object
*/
init: function () {
this.setupHandlers();

EventBus.subscribe(‘event-name’, this.onEventHandler);
},

/**
* @method setupHandlers
* Sets up all those fancy handlers
*/
setupHandlers: function() {
this.onEventHandler = this.onEvent.bind(this);
this.eachItemHandler = this.eachItem.bind(this);
this.onItemClickHandler = this.onItemClick.bind(this);

return this;
}

/**
* @method onEvent
* Handles that event we are subscribed to
*/
onEvent: function (e) {
$(‘.sidebar-heading’).removeAttr(‘style’);
if($(‘.js-items’).length) {
$(‘.js-items’).each(this.eachItemHandler);
}
},

/**
* @method eachItem
* Does exactly what we need to do to each item in that list
*/
eachItem: function(i, el) {
var $element = $(el);
$element.on(‘click’, this.onItemClickHandler);
},

/**
* @method onItemClick
* What to do when one of those items is clicked!
*/
onItemClick: function(e) {
alert(‘clicked’);
}
}
[/javascript]

In addition, after having coded this way for a while, you’ll develop a pattern of having your handlers for many different types of functions (for each callbacks, event callbacks, etc.) grouped together in one part of your code.

The key to becoming a better developer is to practice better development, even when it seems like a waste of time.

Leave a Reply