Introduction to ECMAScript 6, Part 1: Arrow functions, Classes & Object Literals
This article is Part 1 of the ECMAScript 6 series, and it is based on the Codementor Office Hours hosted by Jack Franklin. This article will go through arrow functions, classes, and object literals in ES6.
Arrow Functions
The arrow functions are one of the quick wins of ECMAScript 6. They’re really nice to read with their shorter syntactical form, but they’re also easier for lexical this binding to maintain that reference to the correct scope. Currently in JavaScript there are a lot of functions like map, reach – we use them in callbacks a lot – we have functions that take other functions as arguments.
The Syntax
var num = [1,2,3];
num.map(function(x) {
return X * 2;
});
Above is a map function where you give it an anonymous function, it takes an argument and you return X times two.This is going to take the array of numbers and return a new array where every number in it has been doubled so we’ll get back 2, 4, 6.
This works, but it is fairly verbose. In Ruby we have a much nicer syntax, we do not have this long function keyword.
Arrow functions bring a much more succinct and nicer approach for writing these kinds of functions. Where you have a function like the map that takes in another function as its argument, you can express the intent of what’s going on much clearer.
num.map((x) => x * 2);
In ES6, the function keyword is replaced by the fat arrow.
Like CoffeeScript, ES6 has a “thin arrow” and “fat arrow” syntax. You do not actually have to return explicitly. Just the last statement in that function is what gets returned – that last expression.
These two bits of code are achieving the same thing but the second is a much nicer syntax.
The Problem with Scopes in ES5
Another difference with the arrow functions which is really important is how the scoping works. JavaScript developers should be fairly familiar with the following problem:
var jack = {
name: 'jack',
friend: ['james', 'steve'],
printFriends: function() {
this.friends.forEach(function(F) {
log(this.name, ‘knows’, f);
})
}
];
// undefined knows james
// undefined knows steve
In the example above, the object jack has a name and an array of friends (james and steve). The printFriends function goes over all the friends using the forEach , where logging “ this.name ” should point to my name, Jack and then to the string “ knows “, then the name of the friend. So, whenever I call that function I can expect this function to print out “jack knows james” and “jack knows steve.”
However, the problem here is the function ends up logging “undefined know James” and “undefined knows Steve” instead. This has to do with the rules of what this function’s scope is when it is invoked. In this case, the function deposit forEach is going to be a invoked such that its scope (the keyword within that function) isn’t the jack object as we expect, but it’s instead the global object. If we are working the browser, that would be the window. If we are working in Node or iOS, that would be the route global object.
This is a really common problem and it’s probably one of the biggest ones and it stumps beginners to the language. It all has to do with the rules of what the scope is of the function and it depends on how it’s invoked.
Lexical Scoping in ES6
Arrow functions solve the scoping problem—I can write the same code described above and simply swap out the function keywords for my arrow function. This is called lexical scoping.
var jack = {
name: 'jack',
friends: ['james', 'steve'],
printFriends: function() {
this.friends.forEach((f) => {
log(this.name, ‘knows’, f);
})
}
};
// jack knows james
// jack knows steve
So, when the function is called, the scope of the function will be the same scope the function was defined in.
We’re defining the this`` within the printFriends function of the jack object (which is a mouthful), and this is kind of bound to the jack object. When forEach goes over each element and it calls this function, the scope of that function is the object. We can do “this.name” just like we did before, but the fat arrow function has kind of kept that scope. Now we get “jack knows james” and “jack knows steve.”
This has been motivated by CoffeeScript, which provided this fat arrow function that does this same job of dealing with the scope. Thus, while most people say that arrow functions are equivalent to the function keyword, they almost are, with the addition that we have the fat arrow here which does the scoping too.
A Typical Scoping Use Case
People come to JavaScript for something like jQuery or some other library. One of the first things people do is to make requests to APIs (e.g. Twitter APIs, Facebook, Instagram, whatever it may be. They typically pass in callbacks of things like a getJSON function which makes HTTP requests to get some data. Again in here, “ this.name ” isn’t going to refer to jack because of the scope when that callback function is called.
var someAjax = {
name: 'jack',
get: function() {
$.getJSON(url, funtion(d) {
log(this.name, d);
})
}
};
But then I sort that out for an arrow function, and now this.name does point to jack as expected.
var someAjax = {
name: 'jack',
get: function() {
$.getJSON(url,(d) => {
log(this.name, d);
}) }
};
Things to Note about Arrow Functions
When the arrow function is on one line, you do not need the curly braces while mapping the example. If I moved the log from the example above onto the same line as the fat arrow, I could omit the curly braces.
However, as soon as you want to split the arrow for multiple lines, you do need those braces.
Classes
A bigger feature coming to JavaScript and ES6 is classes. Some people have called these one of the worst features to come to JavaScript, while others think classes are really good. I kind of sit roughly in the middle. I’ll try to explain why.
This is what a class would look like in JavaScript:
class Person {
constructor(name, age) {
this.name = name,
this.age = age
}
about() {
log(this.name, this.age);
}
};
In the example above, I have a “ Person ” class. It has a constructor that takes a name and an age. It also sets the name and age properties. Then, I defined a method called “ about “, which just logs the name and age.
The first thing about the syntax you’ll notice is the new definition for how I’m defining the functions, both constructor and about. There’s no function keyword, there’s no arrow. You just kind of enter the name, put parentheses and then curly brackets to define by value.
You don’t have to type out “function” or any of that, which is much nicer. It makes defining these things much quicker and you’re less likely to slip up by missing a trailing comma or missing a semicolon or just typing out these big long function keywords which do get kind of dull over time. This is less repeated keywords throughout the file and you can see more easily what’s going on.
var jack =
new Person('jack', 22);
jack.about();
//=> jack 22
Now I’ve got arrows going through the code. We have the constructor—this is what it is called when you do “ var jack = new Person ” and you get that argument. The constructor is like the function that gets run when you first initialize an instance of this class.
We can define the other methods such as “ about ” and use that through new Person (‘jack’, 22) (which calls the constructor). Then we can call “ jack.about ” to get “ jack 22 ” – my name and age.
Key Takeaways:
- This isn’t like a new inheritance system or class-based system. It is all using the prototypal system we are used to in JavaScript, and we’re just adding a nice syntactical sugar on top of it. This does not mean that you should go ahead and replace your object literals with classes all over the place.
- In some cases, the classes will lead to your code being more readable and easier to work with. In other cases, keeping things as objects like we always have done prior to having classes is just as good as well. For example, if you have a complicated object that has to do a lot of things, building a class is probably going to help structure that and let it see what’s going on. In other cases, it might be just a bit over the top and a bit more than what you might need.
Class Inheritance
ES6’s class inheritance uses JavaScript’s prototype, so there’s nothing new going on here, it’s just the sugar.
class Son extends Person {
constructor(name, age) {
super(name, age);
this.son = true;
}
};
var jack = new Son(‘jack, 22);
jack.about(); //=> jack 22
jack.son; //=> true
In the example above, the class called _ Son _ extends the Person class. We call the constructor, and _ this. _ will override the constructor on the Person class. When I create a new instance of _ Son, _ it will call the _ this. _ constructor.
Then I can call _ super _ to call the method of the same name on the parent class. Calling super(name, age) will call the constructor on the person class passing that name and age. I’ve also set an extra property—in this case I’ve got “this.son” to equal true.
Now, if I create a new _ son _ instead of creating a new _ person _, you can see that I can still call that “about” method because that is defined in the _ person class _ and _ son _ in inheriting from that. Thanks to the extra property set earlier, I can also check this new property _ son _ and it is set to true.
Object Literals
I showed you the new way of defining functions and classes and we have that too in object literals now. So there’s no longer any need to do about code on function brackets and the curly braces in the function definition. You just kind of omit that middle function keyword and just type “about.”
var jack = {
name: 'jack',
age: 22,
about() {
log(this.name, this age);
}
};
And this just works exactly as you would expect. It’s worth noting it’s still just a regular function. It’s not being defined with an arrow function or anything so it’s not getting that additional scoping stuff. It’s just a plain old function without the function keyword.
You can also define a property within an object, wrap it in square braces, and define it using dynamic parts:
var jack = {
{'hello_' + (() => 'world')()]: 42
};
console.log(jack.hello_world); //42
In the example above, I am naming a property _ “hello,” _ but then also giving it a function that returns ‘ world ‘, which I am going to need to evaluate. So this is going to go ahead and define the property _ “hello_world” _ on the _ jack _ object and set it to 42.
By wrapping this property in square braces, you can add dynamic sections that’ll be figured out when this object is defined. It’s pretty funky, and I still can’t think of a good use case for but I love the idea.
syntax error in,
var jack = {
{‘hello_’ + (() => ‘world’)()]: 42
};
console.log(jack.hello_world);
change it to
var jack = {
[‘hello_’ + (() => ‘world’)()]: 42
};
console.log(jack.hello_world);
Great Post
Thanks a lot it helped me a lot
I am also going to share it to my friends and over my social media.
Also,
Hackr.io is a great platform to find and share the best tutorials and they have a specific page for EMCAScript
This might be useful to your readers: https://hackr.io/tutorials/…
I don’t know what you are trying to explain by putting wrong code samples.
You need to redo it.