August, 2018
An introduction to some of the new features in ES2015

Browser support

ES2015 was a big update to JS. To see a comprehensive list of support for ES2015 see

Because ES2015 isn’t fully supported it should be transpiled to ES5. Babel is the most popular transpiler. For small projects you can link to a Babel transpiler and transpile in the browser. However, this is slow and not recommended for working on large projects or for production.

const and let

const and let allow you to define block scoped variables. Scope hoisting allows you to assign a variable before it is declared. But you cannot do this with const a and let because of the temporal dead zone. While var is hoisted to the top of the scope, let and const are scoped hoisted to the nearest block.

x = 5; // assignment 
console.log(x); //using it
var x; // declaring it

y = 5; // assignment 
console.log(y); //using it
let y; // declaring it

let z = 15;
if(z > 12) { let z = 5; } // the value of z isn't changed globally. 

As you can see let allows you to declare variables that are limited to the statement, block, or expression.

Const creates a read-only reference to a variable but it’s not immutable.

const dozen = 12; 
const dozen = 13; // will not work 

if (dozen === 12) {
  let dozen = 13; // this will work because JavaScript is block scoped
console.log(dozen); // dozen is still 12

Template literals

These are also called template strings or string literals.

const name = "Brad";
const thing = "dog";
const sentance = `I am a ${thing}, my name is ${name}`;

Template literals preserve white space and line breaks. They also allow you to include expressions.

Tagged Template Literals

function tagTransformer(strings, ...values) {
  let res = "";
  values.forEach((val, i ) => { values[i] = val.toUpperCase()});
  res = strings.reduce((acc, val, i) => {
    return acc + values[i - 1] + val;
  return res + "!";

let description = "great";
let kind = "chocolate chip";
let transofrmed = tagTransformer`This is a ${kind} cookie, and it is ${description}`;


Arrow functions

let add = (a) => { return (b) => (a + b) };
let add5 = add(5);
console.log(add5(15) === 20);

Arrow functions do more than provide a cleaner syntax. They also help work around the problem of this. Arrow functions don’t have their own this. They inherit this from the enclosing scope. However, they may complicate debugging since they have an anonymous stack trace. Arrow functions bind to the scope of where they are defined, not where they are called. This is called lexical binding.

Rest and Spread Operators

Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.

Mozilla Spread

Mizilla Rest

Below the array is spread as individual arguments in the function call. But in the function definition, all of those arguments are collected into an array.

function foo(x, ...argz) {
  console.log(`The first argument is: ${x}`);
  argz.forEach((x) => {console.log(x)});

let argz = [1,2,3,4,5,6,7];

Default Arguments

function error(m) {

function foo(arg = error(`You seem to have forgotten something.`)) {
  console.log(`So glad you remebered the arg: ${arg} `);

foo({a: 36});

function bar(options) {
  options = Object.assign({ size: 'large', color: 'blue', speed: 'fast' }, options)
  for (let option in options) {

bar({ size: 'medium' });

Object Initializer Shorthand

When the variable has the same name as the object property you can use shorthand syntax.

const name = 'foo', age = 25;
const obj = {name, age};

Object Destructuring

Mozilla Destructuring Assignment

Object destructuring is one of my favorite features of ES2015.

function foo(options = {size: 'large', color: 'blue', speed: 'fast'}) {
    let { size, color } = options  ;
    console.log(size, color)


function bar({ a = 4, b = 5, c = 6 }) {
  console.log(a, b, c);

  a: 1,
  b: 2,
  c: 3

  a: 1,
  c: 3


Concise Properties and Concise Methods

let foo = "24";

var obj = {
  b() {},

Computed Properties and Computed Methods

let foo = "24";
let bar = "foo";

var obj = {
  ["my" + bar.toUpperCase()]() { console.log(this)},
  [bar.toUpperCase()]: 46,


Symbols are a new data type. Symbols can be created and used as object properties. The most useful symbols are the built in symbols. They can be used to change the default behavior of certain operations.

  • Symbol.iterator
  • Symbol.toStringTag
  • Symbol.toPrimitive
  • Symbol.isConcatSpreadable
let a = [1,2,3];
let it = a[Symbol.iterator]();
let x =;
while (x.done === false) {
  x =;

let b = {
  *[Symbol.iterator]() {
    for (let k in this ) {
      yield this[k];
  1: 1,
  2: 2,
  3: 3,

let a = [ ...b ];

For Of Loop

The for...of loop can iterate over any iterable.

Mozilla For…Of

let a = [1, 2, 3, 4, 5];

for (let v of a )  {


Generating iterators.

let animals = {
  monkey: 1,
  horses: 3,
  cows: 4,
  sheep: 10,
  moose: 3

function* gen(obj) {
  for (let k in obj ) {
    yield `${obj[k]} ${k}`;
let it = gen(animals);
let x =
while(x.value) {
  x =;


Returns the first matching element in an array.

let a = [..."isastringanarray"].find((el) => { return el === "a"});


The Object.assign method copies properties from one or more source objects to a target object specified as the very first argument. This will take two objects and merge them in a 3rd object. Duplicate values from the rightmost object will override those to the left. You can have more than two objects.

let odd = {
  one: 1,
  thee: 3,
  five: 5

let even = {
  two: 2,
  four: 4,
  six: 6

let count = Object.assign({}, odd, even);


Use Maps when the keys are unknown until runtime. Use Maps when all keys are of the same type and all values are of the same type. If there are mixed types use Objects. Maps are iterable.

  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.
  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion.
  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.
  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion and iterating over them.
  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you’re not careful. As of ES5 this can be bypassed by using map = Object.create(null), but this is seldom done.
  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.

Mozilla Maps

let count = {
  one: 1,
  two: 2,
  thee: 3,
  four: 4,
  five: 5,
  six: 6

let map = new Map();
for (let k in count) {
  map.set(count[k], k);


A WeakMaps keys must be objects, the values can be anything.

One difference to Map objects is that WeakMap keys are not enumerable (i.e., there is no method giving you a list of the keys). If they were, the list would depend on the state of garbage collection, introducing non-determinism.

Mozilla WeakMaps

This makes WeakMaps useful for protecting private information.

WeakMap keys may be garbage collected, also the values the keys reference may be garbage collected as well. This make them memory efficient.


If a map is comparable to an object, then a set is comparable to an array. However, a value in a set must be unique.

let count = {
  one: 1,
  two: 2,
  thee: 3,
  four: 4,
  five: 5,
  six: 6

let set = new Set();
for (let k in count) {


WeakSets are similar to week maps, they only store objects. The objects are garbage collected. You cannot enumerate a WeakSet. A week set has three methods, add, set, and delete.


Classes are syntactic sugar over JavaScript’s prototypal inheritance.

The static keyword defines a static method for a class. Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.

Mozilla Classes

When a class extends another class.

If there is a constructor present in the subclass, it needs to first call super() before using “this”.

Mozilla Classes

class Store {
  constructor(name, address) { = name;
    this._address = address.toUpperCase();
  get address() {
    return this._address;

  set address(address) {
    this._address = address.toUpperCase();

class Bakery extends Store {
  constructor(name, address, specialty) {
    super(name, address );
    this._specialty = specialty;

  get specialty() {
    return this._specialty

let goldenBuns = new Bakery("Golden Buns", "123 Wheat St", "Bread");



JavaScript is single threaded and thus synchronous by nature. Promises provide a mechanism for writing asynchronous code.

let url =

function requestURL(url) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();"GET", url);
    xhr.onload = () => resolve(xhr.response);
    xhr.onerror = () => reject(xhr.statusText);

  .then(results => {
    document.body.innerHTML = results;
  .then(() => {
    console.log("Then the page was updated.");
  .catch(error => {
    console.log(`Error: ${error}`);
  .finally(() => {
    console.log("Finally, it's over.");


The export statement is used when creating JavaScript modules to export functions, objects, or primitive values from the module so they can be used by other programs with the import statement.

Exported modules are in strict mode whether you declare them as such or not. The export statement cannot be used in embedded scripts.

Mozilla Export

You cannot export default, variables defined with var, let, or const.

export default myFunction;
import myFunction from './myJsFile.js'

There are other module syntaxes in JavaScript. Node.js uses common.js modules.

const myFunction = require('myfunction');

You can read more about various module syntaxes here: ES6 Modules