One of many favourite things in ECMAScript 6 (aka ES6) is the newly added arrow functions. If you’re a Coffeescript user then the arrow function premise is not entirely new to you.
Essentially it allows you to create an anonymous function with the contextual value of “this” being the scope of the function being that of the outer function that the arrow function is being defined in. Nor does it expect you to use the keyword function
Still with me? Here is an example using the classic jQuery DOM ready shorthand.
[code]
$(() => {
// jQuery document ready
});
[/code]
Does this look familiar? No?
[code]
$(function() {
// Document.ready
});
[/code]
How about now?
As you can see the arrow function literally just creates an anonymous function without needing to write function() {}
– while on the surface it might not appear to be that useful, but the amount of typing and scope headaches this will probably save you in your lifetime is quite a lot.
The benefit of arrow functions is especially useful when dealing with callbacks for functions such as; map, filter or anything else that uses a callback function. They are considerably easier to read and faster to type.
Magical context binding unicorn =>
If you’re familiar with how scoping works in Javascript, then you would have faced the dreaded buried context caveat of Javascript in which a function inside of another function means the outer scope cannot easily be accessed from the inner, unless you created a that
variable or use bind.
Still confused what I am talking about? Let me show you the issue first hand.
[code]
function letsDoSomething() {
this.someVariable = “someValue”;
asyncFunction(‘fetchUsers’, function() {
// The value of “this” is different to that of the outer this
// So the someVariable we defined above can’t be accessed any more
});
}
[/code]
The outer scope above is lost because of this newly introduced anonymous function on the callback of our async function. One of the existing solutions we might use is create a reference to the outer scope and call it that
but it is a pretty hacky solution.
[code]
function letsDoSomething() {
this.someVariable = “someValue”;
// Create a reference to this outer scope
var that = this;
asyncFunction(‘fetchUsers’, function(response) {
// We can access the outer scope thanks to the that variable we created
console.log(that.someVariable); // Will print someValue
});
}
[/code]
For those who were aware of it and didn’t have to support IE8, we could use the less messy approach of bind
[code]
function letsDoSomething() {
this.someVariable = “someValue”;
asyncFunction(‘fetchUsers’, function(response) {
// We can access the outer scope thanks to the that variable we created
console.log(this.someVariable); // Will print someValue
}.bind(this));
}
[/code]
What we did with bind
is we told the callback function to use our own supplied scope which is the outer this
instead of creating an entirely new scope (silly anonymous function). While nicer, it still is a hack and bind has some performance issues to worry about.
So where am I going with this? Arrow functions by default will set the inner scope context to the outer scope context it was defined within. So lets rewrite the above example and use an arrow function instead to see the magic.
[code]
function letsDoSomething() {
this.someVariable = “someValue”;
asyncFunction(‘fetchUsers’, (response) => {
// We can access the outer scope thanks to the that variable we created
console.log(this.someVariable); // Will print someValue
});
}
[/code]
Much cleaner, wouldn’t you agree? We didn’t have to use bind
or create a variable, or use call
or apply
and our context assumes the outer context within by default which in 99% of all use-cases is what you would want to happen.
Single line arrow functions () =>
When dealing with a single line arrow function, returns are implicit. This means that a one line equation (say checking if a number is greater than 5) will return by default.
Another example:
[code]
var arr = [1, 2, 3, 7, 8, 12, 15, 20];
arr.filter((val) => val > 5 );
[/code]
If you try the above code out, you will get the following array returned; Array [ 7, 8, 12, 15, 20 ]
Breaking down the example for you, it might make more sense at first if you were to see it the traditional ES5 way.
[code]
var arr = [1, 2, 3, 7, 8, 12, 15, 20];
arr.filter(function(val) {
return val > 5;
});
[/code]
This is exactly the same as the arrow function example above, except we didn’t have to write function
or return
how good is that?
Multi line arrow functions () => {}
Exactly the same thing as single line arrow functions with exception of the return NOT being implicit. You can use the above example, but you will have to write return
but still not have to worry about writing function
Last example (I promise)
[code]
var arr = [1, 2, 3, 7, 8, 12, 15, 20];
arr.filter((val) => {
return val > 5;
});
[/code]
As you can see we had to write return
and we used some curly braces as we would any normal function declaration. Pretty awesome, right?
ES6 support and transpilers
Okay, so at the moment (as of January 2015, for those in the future) support for ES6 is pretty fucking horrendous. Firefox supports a lot of the ES6 spec already, Chrome basically supports nothing (in the stable branch anyway) and the most surprising of all, Microsoft’s upcoming Spartan browser (aka renamed Internet Explorer) has the best ES6 support of them all and its only a technical preview.
So what I am trying to say is, even though most browsers don’t support ES6, through the use of a transpiler we can convert out ES6 code to ES5 compatible code for older browsers. My personal preference is 6to5, but Traceur is also pretty good as well.
Conclusion
Arrow functions => are awesome. They save us considerable time and effort and as developers, we are all aiming for that high efficiency mark, right? Eliminating room for error and in some cases, making our code more readable.
Hi,
then how to access `this` from inside jquery callback that reference to element itself, not scope of class?
example
$(document).on('change', 'select[name="province"]', () => {
let provId = $(this).val() // how to access `this` that reference to original element (select)
this.getCities(provId)
})
How do I refference the value of brand from the outer scope here
const car1 = {
brand : ‘Porsche’,
getCarDescription : (cost, year, color) => {
console.log(`This car is a ${this.brand}. The price is $${cost}. The year is ${year}. The color is ${color}. \n`)
}
}
car1.getCarDescription(180000, 2019, ‘dark’);