Categories:

Arrow function usage, lexical binding of "this"

Arrow functions with their ultra lean syntax are a natural replacement for their traditional anonymous counterparts. To explicitly invoke an arrow function, as with a regular anonymous function, you would assign it to a variable first:

var weightonmars = earthweight => earthweight * 0.38
weightonmars(100) // returns 38

You can also invoke arrow functions immediately upon their definition using the Immediately Invoked Function Expressions pattern (IIFEs). Simply wrap the entire arrow function in parenthesis and call the function using () at the end:

( earthweight => earthweight * 0.38 )(100) // returns 38

Here is another example:

var trianglearea = ( (b, h) => { // returns 50
	var area = (b * h) / 2;
	return area;
})(20, 5)

The IIFEs pattern is used commonly with regular anonymous functions to instantly create a local scope for all variables and methods contained inside it, so they do not pollute the global namespace.

Calling arrow functions like regular functions can be useful, but the real appeal is in using them in places where anonymous functions normally reign, as call back functions inside event handlers, prebuilt JavaScript methods and inside your own functions. For example, many JavaScript Array methods take an anonymous function as an argument to handle the returned result. Replacing that with an arrow function makes for a much more succinct affair:

Sort an array numerically- Arrow function syntax
// sort array numerically and descending
var ages = [25, 8, 7, 41];
var oldfirst = ages.sort( (a, b) => b - a );
Regular function equivalent
// sort array numerically and descending
var ages = [25, 8, 7, 41];
var oldfirst = ages.sort( function(a, b){return a - b} );

The first version as you can see is a lot more compact. Continuing with the array theme, the following capitalizes the first character of each array element using Array.map() and an arrow function as callback:

// capitalize first character of each array element value
var names=["george", "Edwin", "jane", "mary"];
var result = names.map( name => {
	var modified = name.charAt(0).toUpperCase() + name.substr(1);
	return modified // return name with first letter capitalized
})

And finally, just to convince you of the natural pairing between JavaScript Array methods and arrow functions, the following uses both to sort an array of object literals based on a particular object literal property, in this case, the birthday field so the objects are sorted by birthdays (oldest to youngest):

var employees=[
	{name:"George", birthday:"March 12, 1978"},
	{name:"Edward", birthday:"June 2, 1950"},
	{name:"Christine", birthday:"December 20, 2000"},
	{name:"Sarah", birthday:"April 30, 1945"}
];

var oldestfirst = employees.sort( (a, b) => {
	var dateA = new Date(a.birthday);
	var dateB = new Date(b.birthday);
	return dateA-dateB //sort by birthdate oldest first
});

- Lexical binding of the this object inside Arrow Functions

Arrow functions are just like regular anonymous functions, with one major exception- the behaviour of the this object inside the function. In a regular function, the value of "this" is context based- call the function inside a link, and "this" points to the link's object; call it inside setInterval(), and "this" points to the global window object etc. This behaviour of traditional functions while robust is often unnecessary and a source of errors for developers not appreciating fully how "this" works or simply through carelessness. For example, the following example attempts to call the start() method of a custom object to increment its counter property by 1 every second, though it fails due to incorrect assumption of the "this" object reference:

var countup = {
	counter: 0,
	
	start:function(){
		setInterval(function(){
			this.counter++; // **INCORRECT**- doesn't increment countup's counter property
		}, 1000);
	}
};

countup.start();

In the above the code ths.counter fails to properly reference the counter property of the countup object, though the error may not be so obvious to spot. The coder either mistakenly or carelessly thought "this" points to the countup object, when in fact it points to the global window object due to the context "this" is being called- inside the global window method setInterval(). The result is a reference to a non existent window.counter property that will repeatedly return NaN when we try to increment it. To properly reference the countup object then inside the anonymous function, we should cache a reference to the correct "this" object before the context changes to a different one:

var countup = {
	counter: 0,
	
	start:function(){
		var countup = this; // cache reference to the countup object
		setInterval(function(){
			countup.counter++; // NOW CORRECT- increments countup's counter property
		}, 1000);
	}
};

countup.start();

Now the code works as expected.

Arrow functions deviate from regular functions in the way the "this" object inside them behaves; the goal here was to simplify and eliminate a common source of mistakes when referencing "this" inside an anonymous function. The "this" object inside a arrow function is lexically bound, which is just a fancy way of saying its value is static and determined by the scope "this" is defined in. Contrast that with regular functions, where "this" is dynamic and based on the context it's called regardless of the scope at the time "this" was defined. Lets take the previous example that gave us trouble initially, and see how changing over to using an arrow function intuitively addresses the problem:

var countup = {
	counter: 0,
	
	start:function(){
		setInterval( () => {
			this.counter++; // Increments countup's counter property
		}, 1000);
	}
};

countup.start();

Since the "this" object inside the arrow function is bound at the time of definition based on the scope it's part of (in this case, the countup object), the code above works right out of the box without worrying about the context in which "this" is called. As another example, lets create a constructor function with a method that's called each time a HTML element is clicked on. Using an arrow function as the event handler's callback function takes the guess work out of accessing the constructor function to properly reference its method:

function clickslideshow(imgid, imagearray){
	this.imgref = document.getElementById(imgid)
	this.images = imagearray
	this.currentimg = 0
	this.imgref.addEventListener('click', () => {
		this.rotateimage() // lexical binding of this
	})
}

clickslideshow.prototype.rotateimage = function(){
	this.currentimg++
	if ( this.currentimg == this.images.length) // wrap around index value
		this.currentimg = 0
	this.imgref.src = this.images[ this.currentimg ]
}

new clickslideshow('myimage', ['1.gif', '2.gif', '3.gif'])

The above function changes the image with ID "myimage" to a different one each time it's clicked on, cycling through a list of image URLs. When clickslideshow  is instantiated, it attaches a "click" event to the target image so that when it's clicked on, the object method rotateimage() is called to change the image. Because the "this" object inside an arrow function is based on the scope it is defined in, in this case the clickslideshow object instance, this.rotateimage() accesses the rotateimage() method of clickslideshow instance. If we had used a regular anonymous function instead, "this" would have been context based and pointed to the DOM Image Element, not what we want in most cases when we're invoking "this" inside custom objects.

-Other Considerations

Some other considerations before we set you loose with Arrow Functions. As mentioned in the introduction, Arrow Functions cannot act as constructor functions (using the keyword new), so the following would return an error:

var poorconstruction = x => x-1;
new poorconstruction (5); // TypeError: not a constructor

Also, not only is the "this" object lexically bound inside Arrow Functions, but it also cannot be modified. This differs from regular functions, where calling the function using bind(), call() or apply() allows you to modify the "this" object at the same time, for example:

var evthandler = function(){ alert(this) };
window.addEventListener('click', evthandler.bind(document), false); // alerts document instead of window

Calling bind(), call() or apply() on an Arrow Function still works, though has no effect on its "this" object:

var evthandler = () => alert(this);
window.addEventListener('click', evthandler.bind(document), false); // always alerts window, the scope when evthandler() was defined

And last but not least, Arrow Functions can be identified the same way functions are, such as using instanceof Function or typeof:

var evthandler = () => alert('hi');
evthandler instanceof Function // true
typeof evthandler // function

And with that you now have all the info you need to start using Arrow Functions, well, as soon as they become widely supported across modern browsers that is.

End of Tutorial

Partners