The newest iteration of the Javascript language is just around the corner. As of June 2015 the spec for ES6/ES2015 has been approved. Because of that there will be a lot of new features and syntax coming to a browser near you!
Lets break down some of the Easy Wins from the new version. When I say Easy Wins I mean things that do not require a large amount of research to start working with. For example we will not be talking about Generators or Spread Operators, I think those require a more in depth post.
Some of the things thing we will go over are:
-
let
-
const
- Template Strings
- Classes
- Arrow Functions
- Promises
Lets talk about let
In JS currently we have function scope. Meaning if a variable is created in a function using the var
keyword it is bound to that function. It is not available outside of that function. It is available inside of the function it was created in, and any functions created inside of that.
In some other languages there is also this idea of block scope. Meaning anything declared between the {}
characters, or a block, would only be scoped to that block. So we can have scopes that are not function dependent.
In JS it is common to write a for
loop like such:
for(var i = 0; i < 10; i++) {
console.log(i);
}
console.log('After:', i); //10
With the initial thinking that the var i = 0
is creating a variable called i
that is scoped only for this statement. However that is not true, if you console.log(i)
after the for
loop you will see that it is now available to you! This is maybe not the behaviour you are expecting.
In ES6 there is a new keyword called let
which will give you the ability to create a variable that will be scoped to the block statement in which it was created.
for(let i = 0; i < 15; i++) {
console.log(i);
}
// i undefined
console.log('After:', i);
Use the let
keyword instead and try to console.log(i)
outside of the for
loop will now give you an undefined
. This will work in any block statement, so anything in between {}
. This includes inside of closures.
function myFunction() {
let a = 'Hey';
return (function() {
return a;
})();
}
Calling myFunction();
will return to you "Hey"
. Or maybe inside of an if
statement.
if(true) {
let name = 'Ryan';
}
console.log(name);
In this case the name
variable is bound just to the if
statements scope.
Const
Currently inside JS we are not able to create any variables that can not be changed, or that are immutable. We have a convention where a variable that is in ALL CAPS is supposed to be a value that should not be altered. This should be a constant variable.
ES6 offers us the new const
keyword which allows us to create a read-only constant value.
const CONFIG = 'Configure'
CONFIG = 'New String' //Silent error
console.log(CONFIG); //Configure
After a const
value is created you can not mutate it, or change it. If you attempt to change the value it will fail silently. The const
value is also block scoped and works like the let
keyword.
Example:
const API_KEY = 'h879u3iu1789123';
function apiCall() {
//Do some ajax call/whatever with API_KEY
return data;
}
This is a pretty trivial example, but hopefully the idea comes across.
Template Strings and String Interpolation
Dislike concatenating strings together? In ES6 we can now use template strings. The syntax is very straight forward, instead of using quotes ''
and the +
, you use the `
characters. Also known as a back tick. Typing something like `Hey there`
will just create a string that says “Hey there”.
You might be thinking, “Oh cool…but what about the + portion”. There is a new syntax that looks like this ${variable}
that we can use in place of the verbose concatenation syntax. Consider this, if we have a variable let name = "Ryan"
and we want to concatenate it into a string it might look like this:
var name = "Ryan";
var string = "Hi my name is " + name + " and I live in Toronto";
With the new ES6 template string syntax we can use the back ticks and the ${}
expression to concat a string together with the variable name
.
var name = "Ryan";
var string = `Hi my name is ${name} and I live in Toronto`;
This works great when you maybe have an object, or a lot of information that you need to concatenate together.
var data = {
name: "Ryan Christiani",
age: 29,
location: 'Toronto, ON'
}
var string = 'Hi my name is ' + data.name + ' and I am ' + data.age + ' and I live in ' + data.location;
console.log(string);
//Hi my name is Ryan Christiani and I am 29 and I live in Toronto, ON
Using template strings:
var data = {
name: "Ryan Christiani",
age: 29,
location: 'Toronto, ON'
}
var string = `Hi my name is ${data.name} and I am ${data.age} and I live in ${data.location}`;
console.log(string);
// Hi my name is Ryan Christiani and I am 29 and I live in Toronto, ON
Classes
In most programming languages there is this idea of a Class. A class is some structure that is used as a blue print for creating an object oriented structure.
Before classes in ES6 there were a couple ways to create this structure. One way would be using a function as a constructor.
var Warrior = function() {
this.hp = 100;
this.str = 5;
this.attack = function(target) {
target.hp = target.hp -= this.str;
}
};
var character = new Warrior();
character.attack(enemy);
Here we use a function and the this
keyword to apply properties to it. However by default inside this function the this
keyword will be bound to the window
object. In order to get it be bound to our Warrior
we need to use the new
keyword when we create it. In this case we are instantiating a new copy of this object.
If you want to use our Warrior
as the base object to create more objects from, we have to use the prototype
property.
var Fighter = function() {
this.weapons = ['sword','shield'];
};
Fighter.prototype = new Warrior();
var myFighter = new Fighter();
Here we use the prototype
property on our Fighter
to make it inherit the properties from the Warrior
object. So our Fighter
has access to properties like the method .attack()
but also has it’s own features, like weapons
.
Now lets look at the new Class type in ES6, a new keyword has been introduced, class
.
class Warrior {
constructor() {
this.hp = 100;
this.str = 5;
}
attack(target) {
target.hp = target.hp -= this.str;
}
};
var character = new Warrior();
character.attack(enemy);
In the class there is a method called constructor()
that is run once you instantiate a new version of the Class. Inside here you can run any sort of initialization code that you need. If you need your class
to accept parameters they are assigned in the constructor()
method.
class Warrior {
constructor(name) {
this.name = name;
this.hp = 100;
this.str = 5;
}
}
And when you instantiate the class you would pass in the values as needed.
var myWarrior = new Warrior('Ryan');
console.log(myWarrior.name); //"Ryan";
With the Class type if you want to make something inherit from another class we can use the extends
keyword.
class Fighter extends Warrior {
constructor() {
this.weapons = ['sword','sheild'];
}
}
This is the same as
Fighter.prototype = new Warrior();
Private properties in classes
You might be thinking to yourself, well we have the class
what about private properties? In ES6 there is a new data type called a symbol
. To make a new symbol we would create it as such.
var mySymbol = Symbol();
Notice the lack of the new
keyword. This will create a new symbol for us to use. A Symbol is a unique identifier, because of this we can use this to set private properties on our class. No two Symbols are the same.
var privateProp = Symbol();
class Warrior {
constructor() {
this[privateProp] = ['some', 'data'];
}
}
Now this property is somewhat private, if you need to access it you use the this[privateProp]
selector.
Arrow Functions
With ES6 we get a new function syntax, the Arrow Function! The syntax is as follows.
() => {
//statements
}
We no longer need the function keyword here, and we use the =>
before the {}
syntax. This will work only for anonymous functions, you can not have a named arrow function.
If you have a function that will accept a single parameter you can actually leave the parenths out.
x => { return x * 2 }
This is great for simple callbacks. You can also leave out the {}
all together in a simple expression if you want.
var add = (a,b) => a + b;
Notice the lack of the return
statement in the one above, excluding the {}
will create an implicit return
There is a lot more syntax available for use that you can find HERE. I think MDN can do a better job of explaining some of the nuances.
I do want to go over the lexical scoping of this
however. When working with functions the this
keyword and its current context can be confusing for some.
var teacher = {
name: "Ryan",
location: "Toronto, ON",
courses: ['Front End Bootcamp', 'Intro Web Development', 'Advanced Web Development'],
print: function(){
this.courses.forEach(function(course,index) {
console.log(`${this.name} teaches ${course}`)
});
}
}
teacher.print();
//teaches Front End Bootcamp
//teaches Intro Web Development
//teaches Advanced Web Development
In the example above the this
keyword is scoped to the object we are working on, with the arrow function we can actually change this context. Note that the callback in the forEach
changes the value of this, it is bound to the window
object instead! With an Arrow function we can make this behave the way we want!
var teacher = {
name: "Ryan",
location: "Toronto, ON",
courses: ['Front End Bootcamp', 'Intro Web Development', 'Advanced Web Development'],
print: function() {
this.courses.forEach((course,index) => {
console.log(`${this.name} teaches ${course}`)
});
}
}
teacher.print();
//Ryan teaches Front End Bootcamp
//Ryan teaches Intro Web Development
//Ryan teaches Advanced Web Development
In our forEach
callback now, the this
value it bound to the scope in which it was created, so the teacher
object.
Lets look at one more example of this so that we can drive the point home.
class Europe {
constructor() {
this.count = 1000;
}
finalCountdown() {
var countInterval = setInterval(function() {
this.count--;
console.log(this.count);
if(this.count === 0) {
clearInterval(countInterval);
}
});
}
}
var gob = new Europe();
gob.finalCountdown();
Maybe we have a class called Europe
and we want to play an intro or something…the one problem here is that when you write setInterval(function(){...});
the this
keyword is scoped to the window. Or the global scope. So the above will print out NaN
.
However if we change the callback function inside of the setInterval
call to be an arrow function, the scope of the function will be that in which it was created, therefore the Europe
class.
class Europe {
constructor() {
this.count = 1000;
}
finalCountdown() {
var countInterval = setInterval(() => {
this.count--;
console.log(this.count);
if(this.count === 0) {
clearInterval(countInterval);
}
});
}
}
This will now print from 1000 to 0!
Promises
In ES6 we get native Promises! A Promise is the eventual success or failure of some action. They are typically used for things like Ajax requests or animations. The idea of a Promise is not new, they have been around for a while. In jQuery we have the $.Deferred()
object that allows us to emulate the behaviours of a Promise, there are other popular libraries like Q that implemented Promises as well.
A Promise can have one of three states, pending, fulfilled, or rejected. We will be looking at the fulfilled and rejected states.
To create a Promise we use the new
keyword and the Promise
constructor. This takes a callback function that accepts two arguments, resolve
and reject
.
var myPromise = new Promise(function(resolve,reject) {
//...
});
These will be used to determine weather our Promise is fulfilled(resolved), or rejected(reject). resolve
and reject
are actually functions that we can use to pass some data along with.
var myPromise = new Promise(function(resolve,reject) {
//do a bunch of stuff
if(something is true) {
resolve('Everything worked');
}
else {
reject('Something went wrong');
}
});
Now that we are either going to fulfill our promise or reject it, we need to do something with this eventual action. Enter the .then()
method, this is a method on the Promise
object we created, it takes two callback functions, the first will be called if the promise is fulfilled, the second will be called if the promise is rejected.
myPromise.then(
//resolved
function(message) {
//Will only be called if it is fulfilled
console.log(message)
},
//rejected
function(message) {
//Will only be called if it is rejected
console.log(message)
}
);
Something to note is that once a promises state has been set, it can not me altered.
What about a situation where you have multiple promises that you need to wait on. There is a method called .all()
that can be used to wait until a certain number of promises have resolved. For example:
var promise1 = new Promise(...);
var promise2 = new Promise(...);
Promise.all([promise1,promise2]).then(data => {
data.forEach((item) => {
console.log(item);
});
});
The .all()
method will take an array of promises and called the .then()
method only once they have all been resolved. If one of the passed promises is rejected, the .all()
method will reject them all.
Using ES6 in your projects today.
This is great and all, but how can you use this today? In order to use ES6 in your projects now, the easiest way to do so is to build it with something like Babel or Traceur. These are transpilers, that take your es6 code and convert it to be es5. This allows us to write with future syntax but allow it to run on current browsers that do not currently support all the features.
gulp-babel
A simple set up would be to use gulp
and the gulp-babel
package.
var gulp = require('gulp');
var babel = require('gulp-babel');
var concat = require('gulp-concat');
gulp.task('js', function() {
gulp.src('*.esnext.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(concat('script.js'))
.pipe(gulp.dest('.'));
});
gulp.task('default', function() {
gulp.watch('*.esnext.js',['js']);
});
Note that with gulp-babel
as of the end of October 2015 will require plugins to transpile your ES6 code. Make sure to run npm install --save-dev babel-preset-es2015
to get the ES6/ECMAScript 2015 plugin!
ScratchJS
Want to just play around in the browser? I suggest ScratchJS from Rich Gilbank at Shopify. It is a simple chrome extension that lets you write in the repl and play with the features in the browser.
ES6 Support
Babel and other transpilers are not able to convert all the features coming, if you need to see if a feature is supported check out this great compatibility table.
Pingback: Call and Apply for Beginners - Ryan Christiani - Front-End Developer - Ryan Christiani – Front-End Developer()