Earlier this week I gave a presentation (I'll share the links for that in a post later today) about the changing nature of JavaScript and as part of that, I quickly mentioned a few modern features that I really dig. One of them was arrow functions. A follower on Twitter asked if I could spend a bit more time on them so I thought I'd whip up a quick blog post. Let me start though with why I absolutely hated them.
Arrow functions have two main aspects. First, they are a new way of writing functions. Secondly, they handle "This" scope issues. The later is something I was fully behind. If you've ever seen that.x = this.x
, then you'll recognize the issue. (And I'll share an example later.) The former though... filled me with deep and utter rage. Ok, I'm being a bit over the top, but at least to me, the syntax change in arrow functions was so radical that I had a mental block just trying to read them. It felt like one of those things developers do to be "cute"/"cool" without any real practical benefit.
Let me share an example.
document.addEventListener('DOMContentLoaded', e => {
console.log('dom loaded like a boss');
}, false);
If you've never seen an arrow function before, that can be near incomprehensible. I'd see code using this form and my brain would come to an immediate stop while it attempted to "rewrite" it in the old school form. In order to really appreciate them, I had to start writing them, and every time I used one, I felt more and more comfortable with it and appreciated the terseness. I said earlier it felt like it was being cute for no real reason, but now it feels as natural as array shorthand (x = [1,2,]).
To understand arrow functions, let's consider an sample function and then rewrite it.
function hello(name) {
return "Hi, "+name;
}
(As an aside, I normally use template literals when building strings in JavaScript now, but I don't want to put too much new syntax in a sample at once.) The code sample above defines a function named hello that takes one argument and has one line of body code. Remember that a function can also be defined like so:
hello = function(name) {
return "Hi, "+name;
}
Ok, so the first thing you do when switching to an arrow function is dump the word function:
hello = (name) {
return "Hi, "+name;
}
Next, you add the arrow.
hello = (name) => {
return "Hi, "+name;
}
And you're done. But arrow functions let you go even simpler. If you only have one argument, you can drop the parens:
hello = name => {
return "Hi, "+name;
}
And if you only have one statement, you can remove the brackets:
hello = name => "Hi, "+name;
Notice I also removed the return. The execution of "Hi, "+name
returns a string and a function will automatically return the last statement result.
Remember though that the only required aspects of an arrow function are the removal of the word function and the addition of the arrow. If that last version looks too short to you, you can add the brackets back in. Also, maybe your function has one statement now but you're pretty confident it is going to grow. For example, you have a function that returns a boolean and for now you just want to return true.
Here is an example of a function that takes two arguments - both before and after:
function greet(greeting, name) {
return greeting + " " + name;
}
greet = (greeting, name) => greeting + " " + name;
Here is another example where the function is multiple lines:
function greet(greeting, name) {
var tempString = greeting + " " + name;
if(tempString.length > 50) tempString = tempString.substr(0,50);
return tempString;
}
greet = (greeting, name) => {
var tempString = greeting + " " + name;
if(tempString.length > 50) tempString = tempString.substr(0,50);
return tempString;
}
I'm also ignoring another modern feature, let
, in the code above, because I don't want to put too much in front of you at once.
Arrow functions work really well, in my opinion, in call backs. The very first example I gave demonstrated this, but here it is again, along with the "old" version:
document.addEventListener('DOMContentLoaded', function(e) {
console.log('dom loaded like a boss');
}, false);
document.addEventListener('DOMContentLoaded', e => {
console.log('dom loaded like a boss');
}, false);
It isn't a huge change, but it feels more compact, and since callbacks like this are littered everywhere in most JavaScript files, the shorter syntax really feels like a boon.
The other benefit of arrow functions are how they correct issues with this
. If you've ever written a call back and realized that this.something inside wasn't working correctly, this is something you'll appreciate. For an example, I'm going to "borrow" the one MDN uses (I'll be linking to it in a moment). Consider this code:
function Person() {
// The Person() constructor defines `this` as an instance of itself.
this.age = 0;
setInterval(function growUp() {
this.age++;
}, 1000);
}
var p = new Person();
If you run this and inspect the value of p
, you'll see that age never increases. That's because the this
in the callback is it's own scope, not the one for the Person function. A common fix was to simply add that = this
and refer to that.age
. But that's smelly. Consider the arrow function version:
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}
var p = new Person();
Boom - just plain works. By the way, if the (() => {
part confuses you, remember that the ()
portion is the argument list, which in this case is empty.
So yeah - I love these things now. I'll be honest - they still don't quite flow off the tongue (or off my fingers) as fast as "old school" functions, but they are getting more and more familiar to me. Alright, how about some resources?
- Once again, MDN has the best documentation.
- Next, check out Dr. Axel's post: ECMAScript 6: arrow functions and method definitions. Note the age of that post - 2012. Yep, these may seem new, but they've been discussed for a while.
- Finally, read this great article by Eric Elliott - Familiarity Bias is Holding You Back: It’s Time to Embrace Arrow Functions
I'd love to hear from my readers. Are you using this yet? If so, what do you think, and if not, why?
Archived Comments
Totally agree that (a,b) => { } was very difficult to read at first. It drove me mad too!
I resisted at first, but am happy to switch to arrow functions if they fix the this/bind nonsense :)
I followed a similar path to you, I think, when it came to arrow functions. At first glance my brain just went, "WTF" and stopped while I mentally translated what it would be in the previous style. Every once in a bit it still trips me up, to be honest. But once I started writing code that used arrow functions, I fell in love, and haven't looked back. That said I still have no idea how to pronounce these things, and I've not seen a lot of consensus from the wider community either. Oh well...!
Editing suggestion for the post:
The lines "The other benefit of arrow functions are how they correct issues with this. If you’ve ever written a call back and realized that this.something inside wasn’t working correctly, this is something you’ll appreciate." don't differentiate JS `this` vs "this" as a word... so it took me a second to understand what you meant. Might want to demarcate the JS `this`.
And a few additional thoughts:
The `setInterval` method works like this too:
```
setInterval(() => this.age++, 1000);
```
... if you're into terseness, anyway, and understand that the engine won't include the "1000" as part of the function body.
Also, if you have a single parameter but are destructuring the argument, the parens have to come back: `([key, value]) => this[key] = value`.
And when you're using the short form with objects -- be sure to use parentheses to avoid ambiguity: `x => ({someKey: x})`
Thanks! (FYI, about the "this" edit - I agree - it will go up next time I deploy the blog.)
I don't use new stuff to be cool, but when they are useful, like most of ES6 updates, them I start to use and recommend them on my code reviews whenever possible.
Fat arrows are nearly indispensable for me. I'm using ES6 classes right now in most of my work. Saying it helps manage "this" is an understatement! Especially when using web components and extending HTMLElement to roll your own, I often want to assign a mouse event to an element inside my component. Having a clean class with a onMouseEvent method inside that knows the scope of the component is huge. The best way to pull this off is to use fat arrows IMO.
https://www.youtube.com/wat... uses .filter, .map and .reduce in his example, and is able to remove even the name of the function itself, instead calling it anonymously.
I'm still not a fan of arrow functions. In my opinion, it makes a function declaration too abbreviated. When I'm searching through many lines of code and looking for a particular function, I like to see the name "function()" actually written. Call me old fashioned but I think it reads much better.
If take a look here https://jsperf.com/arrow-fu..., I might say that there is no difference between arrow and plain functions with only exception when bind is not used.
Sorry - are you saying there is no (real) difference in performance? (Trying to understand your comment and I'm not quite getting it.)
I think one of my favorite aspects of the arrow function is in currying or similar techniques. For example if I want to take a function, return that function and use the returned function for something I would do this in the old syntax:
```
let example = function(prop1){
return function(prop2){
return prop1 + prop2;
}
}
example(1)(2) // returns 3;
```
but when I do that using arrow functions it is a lot more clear.
```
let example = prop1 => prop2 => prop1 + prop2;
example(1)(2) // returns 3;
```
If you don't understand the syntax, to your point above clear could be debated, but when you understand it, the second example is so much cleaner and clearer!
I love it.
Thanks for this post - it's given me understanding and hope I'll get used to arrow functions. I'm getting back into application development after having lived in SQL for the past 15 years, and even then I was using old technologies. I'm now trying to develop a web app and while I have a deep understanding of programming and data, the learning curve of javascript / typescript, react, CSS, git, yarn, npm, node, express and everything else is very steep for an old hand. These arrow functions, "wrongly quoted" strings and lots of other stuff have me tear my hair out. Putting data on a screen and saving it back into a database sure was a lot easier in the good old client server days!