Introduction To Es6

Objectives

  1. Describe the major new features in ES6
  2. Explain how let and const differ from var
  3. Describe how to use arrow functions
  4. Explain the value of the spread operator

Introduction

Up to now, you've probably been working in the browser and on the server with the version of JavaScript known as ECMAScript 5. ECMAScript is an evolving standard, the complete history of which we don't have time to cover here.

But you've probably also heard talk about this thing called ECMAScript 6, ES6, or ES2015 (they kinda missed the deadline on that one). ES6 is the next specification for JavaScript, and it's finally started to appear in a major way in browsers and on servers thanks to Node.js 5.

You can get the complete rundown of ES6 features in Node.js here; but we'll also run through the features that you might see in the upcoming labs here. For our purposes, anything that can be enabled with a simple "strict mode" declaration is fair game — but we won't teach you about the stuff behind the --es_staging or --harmony flag just yet.

Aside: Strict Mode

You might not have encountered it yet (at least knowingly), but ES5 has a way to opt in to a special version of JavaScript called strict mode.

You can read about the ins and outs of strict mode at the link above — generally, it turns silent failures into thrown errors, and it helps prevent variables sneaking into the global scope.

That is, in standard mode, the following code would run — it just wouldn't do anything:

delete Object.prototype;

But in strict mode, this would throw a TypeError:

"use strict";

delete Object.prototype;

Strict mode can be invoked by writing

'use strict';

at the top of the current script — strict mode then applies to the entire script. Alternatively, you can apply strict mode to individual functions:

function strictFunction() {
  "use strict"

  // this will throw an error in strict mode
  delete Object.prototype
}

function standardFunction() {
  // this will silently fail in standard mode
  delete Object.prototype
}

Strict mode does just as its name implies: it enforces stricter rules on the execution of your code. Note that some transpilers (like babel) can set strict mode for you.

Strict mode also enables some newer features that ES6 developers wanted to make sure users explicitly opted in to.

ES6 Features

There are a lot of new features, but for now we'll cover a subset of the new features below.

Block Scoping

let ("use strict")

The keyword let is a new way of declaring local variables. How does it differ from good ol' var? Variables declared with let have block-level scope:

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Scoping_rules
function letTest() {
  let x = 31;
  if (true) {
    let x = 71;  // different variable
    console.log(x);  // 71
  }
  console.log(x);  // 31
}

Notice how x declared outside of the if block differs from the x declared inside the block. Block-level scope means that the variable is available only in the block (if, for, while, etc.) in which it is defined; it differs from JavaScript's normal function-level scope, which restricts the variable to the function in which it is defined (or global/window if it's a global variable).

const

The keyword const does not require strict mode. Like let, it declares a variable with block-level scope; additionally, it prevents its variable identifier from being reassigned.

That means that the following will throw an error:

const myVariable = 1;

myVariable = 2; // syntax error

However, this does not mean that the variable's value is immutable — the value can still change.

const myObject = {};

// this works
myObject.myProperty = 1;

// 1
console.log(myObject.myProperty)

Classes ("use strict")

"Wait," you say. "JavaScript has prototypal, not class-based, inheritance."

You're still right. But classes in JavaScript are awesome, and you'll be seeing them increasingly as ES6 adoption increases.

Consider the following simple example, based largely on the examples from MDN. We want to create a Polygon prototype and inherit from it. We'll start with ES5:

function Polygon(height, width) {
  this.height = height;
  this.width = width;
}

Cool, so we got to the ES6 class constructor, which works just like a constructor in ES5. But now how do we implement the area() getter? Well, not very nicely — let's back up and rewrite what we just wrote:

function Polygon(height, width) {
  this.height = height;
  this.width = width;

  // :(
  this.area = this.calcArea();
}

Polygon.prototype.calcArea = function() {
  return this.height * this.width;
};

const rectangle = new Polygon(10, 5);

console.log(rectangle.area);

Okay, so that worked, but look at how difficult it is to reason about. We have to plan in advance for the properties that we want to set, and area is not calculated dynamically — it's set when the Polygon is instantiated and then forgotten about, so if somehow a Polygon's height and width changed, its area would need to be updated separately. Gross.

Moreover, extending this ES5 Polygon is a bit onerous:

function Square(sideLength) {
  Polygon.call(this, sideLength, sideLength);
}

Square.prototype = new Polygon();

const square = new Square(5);

// Polygon { height: 5, width: 5, area: 25 }
console.log(square);

Well, that seems like it just about works. But what if we check the variable square's constructor?

// [Function: Polygon]
square.constructor;

That's not right. It should be [Function: Square]. We can fix it, though:

Square.prototype.constructor = Square;

const square2 = new Square(6);

// { [Function: Square] constructor: [Circular] }
square2.constructor

Eh, close enough?

(Note: The point here isn't to land on a perfect approach to object inheritance in JavaScript, it's to show that such a goal isn't feasible and won't be achieved in a nice way.)

Now let's try with ES6:

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  // whaaaaat -- getters!
  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.height * this.width;
  }
}

const rectangle = new Polygon(10, 5);

console.log(rectangle.area);

Let's extend it:

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength)
  }
}

const mySquare = new Square(5);

// Square { height: 5, width: 5 }
mySquare;

// [Function: Square]
mySquare.constructor;

// 25
mySquare.area;

Whoa. That was easy.

that was easy

Arrow functions

Arrow functions provide not only a terser way to define a function but also lexically bind the current this value. This ain't your grandpa's JS.

const greet = (greeting, person) => {
  return greeting + ', ' + person + '!';
};

// 'Hello, Marv'
greet('Hello', 'Marv');

var a = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryl­lium'
];

// compare this implementation...
var a2 = a.map(function(s){ return s.length });

// ... to this implementation with the fat arrow
var a3 = a.map(s => s.length);

Fat arrows also have implicit returns — the following are equivalent:

var a3 = a.map(s => s.length);
var a4 = a.map(s => {
  return s.length;
});

If the function only accepts one argument, parentheses are optional:

// this...
var a3 = a.map(s => s.length);

// ... is the same as this
var a3 = a.map((s) => s.length);

If there are zero or two or more arguments, though, you must use parens:

var evens = [1, 2, 3, 4].reduce((memo, i) => {
  if (i % 2 === 0) {
    memo.push(i)
  }

  return memo;
}, []);

Promises

Promises offer a new way of handling asynchronicity.

We'll cover them in greater detail in a later lesson, but for now know that Promise is available globally in Node.js.

Also know that it's awesome.

const promise = new Promise((resolve, reject) => {
  return someIntenseTask().then(result => {
    if (result.success) {
      return resolve(result)
    }

    return reject(result.error)
  })
})

promise.then(result => {
  return doSomething(result);
}).catch(error => handleError(error))

Object literal extensions

ES6 gives us a number of handy new ways to deal with objects. They're features that you either wish JavaScript had, or ones you didn't know you needed.

const prop = function() {
  return "I'm a prop!";
}

const myObj = {
  // computed (dynamic) property names


  // methods
  shout() {
    return 'AH!'
  },

  // short for `prop: prop`
  prop
}

// 'something'
myObj.foobar

// "I'm a prop!"
myObj.prop()

// 'AH!'
myObj.shout()

Spread operator

The spread operator... — is unassuming but incredibly powerful.

We can use it for arrays:

const a = [1, 2, 3]
const b = [0, ...a, 4, 5]

// [0, 1, 2, 3, 4, 5]
b

functions:

function printArgs() {
  // recall that every function gets an `arguments`
  // object
  console.log(arguments);
}

// using `a` from above
// [1, 2, 3]
printArgs(...a);

Template Strings

Template strings in ES6 are most commonly used for string interpolation. Instead of writing:

var foo = 'bar';
var sentence = 'I went to the ' + foo + ' after working in ES5 for too long.';

we can now write:

var foo = 'bar';
var sentence = `I went to the ${foo} after working in ES5 for too long.`;

and we'll get the same result.

You can also use tagged template literals to perform more advanced manipulation:

A tag is simply a function whose first argument is an array of strings and whose subsequent arguments are the values of the substitution expressions (the things in ${}).

Here's the example from MDN:

var a = 5;
var b = 10;

function tag(strings, ...values) {
  console.log(strings[0]); // "Hello "
  console.log(strings[1]); // " world "
  console.log(values[0]);  // 15
  console.log(values[1]);  // 50

  return "Bazinga!";
}

tag`Hello ${ a + b } world ${ a * b }`;
// "Bazinga!"

Destructuring

Destructuring makes it easier than ever to pull values out of objects and arrays and store them in variables. We destructure an array by putting our new variable names at the corresponding index and an object by giving our variable the same name as the key we are interested in.

const [a, b] = [1, 2];
// a === 1 && b === 2

const { a, b } = { a: 1, b: 2 }
// a === 1 && b === 2

To see what other amazing things we can to with destructuring, check out the docs.

Conclusion

There are tons of new features in ES6, and we don't have time to cover them all here. Check out the docs, play around in console, and have fun!

Resources

View Introduction To ES6/ECMA2015 on Learn.co and start learning to code for free.

Unlock your future in tech
Learn to code.

Learn about Flatiron School's Mission

With a new take on education that falls somewhere between self-taught prodigy and four-year computer science degree, the Flatiron School promises to turn students with little programming experience into developers.

In the six months since the Manhattan coding school was acquired by WeWork, it has spawned locations in Washington, D.C., Brooklyn, and London. Now, WeWork is opening a fourth Flatiron School location, this time in Houston.

Adam Enbar, Flatiron School's cofounder, believes now is the time to grow. "How the world is changing has impacted working and learning in very similar ways. We think education fundamentally is about one thing: enabling people to pursue a better life."

Learn. Love. Code.
Students come to Flatiron School to change their lives. Join our driven community of career-changers and master the skills you need to become a software engineer or a data scientist.
Find Us