# Grow with Google: ES6

Source: https://www.andrewzigler.com/feed/grow-with-google-es6
Published: 2018-02-05

Picking up where I left off in [Part 2](), I’m continuing my journey in the [Grow with Google Challenge Scholarship](https://www.udacity.com/grow-with-google) for 2018. Last time I learned all about IndexedDB, but this part of the course taught me ES6, the 6th edition of JavaScript, which is widely used today (sometimes with other names, like ES2015 and Harmony). These lessons gave me a whole new perspective on the language, and they’ve answered many long-time questions I’ve had about the “magic” of JavaScript.

A great part about these lessons is that they were abstract and generic, not focused on a specific project like the lessons before. As a result, it’s easy for me to demonstrate the concepts for you this time!

##### **Syntax**

Instead of using var to declare variables, it’s recommended to use let and const now. Use let when you plan to assign new values to the variable, and use const when you only want to declare it once. Using let and const prevents namespace pollution in the global scope, because variables declared with these commands are limited to the block that contains them. There’s no reason to use var anymore.

It’s now recommended to perform string interpolation with _template literals_. Before, if you wanted to insert variables into a string, it required clumsy syntax, like:

```
console.log('Today is ' + calendar.returnDay() + '.');
```

Now you can use template literals, indicated with backticks (`` ) instead of quotes ('' or ""). They allow you to insert values into a string via ${expression}. Now you can represent that same string like so:

```
console.log(`Today is ${calendar.returnDay()}.`);
```

You can now extract values from arrays and objects through a technique called _destructuring_, whereby you specify the elements to be extracted on the _left_ side of the assignment. For example:

```
const prices = [12, 16, 70];
const [cheap, medium, expensive] = prices;

console.log(cheap, medium, expensive);
```

Produces the following output: 12 16 70

Now thanks to _object literal shorthand_, you can declare objects with simpler code. If there already exist variables within scope that share the same name as the object property, you don’t have to declare both sides of the assignment. You can also drop the function keyword from function declarations inside of an object, as long as you have (). For example:

```
const color = 'red';
const material = 'wax';
const crayon = {
  color: color,
  material: material,
  printDescription: function() {
    console.log(`I'm ${color} and made of ${material}.`);
  }
};

crayon.printDescription();
```

Can be reduced to:

```
const color = 'red';
const material = 'wax';
const crayon = {
  color,
  material,
  printDescription() {
    console.log(`I'm ${color} and made of ${material}.`);
  }
};

crayon.printDescription();
```

And they both output: I'm red and made of wax.

Next, we now have the superior _for…of_ loop to save us from the drawbacks of _for_ and _for…in_ loops. The _for_ loop requires a counter and an exit condition, wheras the _for…in_ loop removes those requirements by using the index. But using the _for…in_ loop makes a mess because if you add a new method to the prototype, it will appear in the loop. But the _for…of_ loop solves these problems with very similar execution as _for…in_, but without the drawbacks. Let’s start with a _for_ loop:

```
const nums = [1, 2, 3, 4, 5];
for (let i = 0; i < nums.length; i++){
  console.log(nums[i]);
}
```

Here’s the same loop, but as a _for…in_ loop:

```
const nums = [1, 2, 3, 4, 5];
for (const index in nums){
  console.log(nums[index]);
}
```

And finally, here’s the same loop as the superior _for…of_ loop:

```
const nums = [1, 2, 3, 4, 5];
for (const num of nums) {
 console.log(num);
}
```

They all produce the same output:

```
1
2
3
4
5
```

The _for…of_ loop only iterates over values in the object, so you’re free to add methods. You can also pass over certain values during iteration with the continue command. For example:

```
const nums = [1, 2, 3, 4, 5];
for (const num of nums) {
  if (num % 2 === 0) {
    continue;
  }
  console.log(num);
}
```

Will produce the following output:

```
1
3
5
```

We now have _spread_ and _rest_. They’re written with ... and to start, _spread _allows you to _spread_ iterable objects into multiple elements. For example:

```
const children = ['Tabatha', 'Riley', 'Jordan'];
console.log(...children);
```

Will output:

```
Tabatha Riley Jordan
```

You can use the _spread operator_ to concatenate arrays easily. For example, if you have two arrays and want to combine their entries:

```
const children = ['Tabatha', 'Riley', 'Jordan'];
const cousins = ['Brandon', 'Ysabel', 'Kyle'];
const family = [...children, ...cousins];

console.log(family);
```

Will output:

```
["Tabatha", "Riley", "Jordan", "Brandon", "Ysabel", "Kyle"]
```

Just remember that this is an operator, not a reference, so if you later make a change to either the children or cousins array, it will not update the assigned variable (family in this case) because the operation only happened _once_ at assignment.

To balance _spread_, we have the _rest parameter_, which allows you to represent any amount of elements as an array. This is useful for destructuring and building variadic functions. For example, you can use it to accept a limitless amount of arguments for a function:

```
function multiply(...nums) {
  let total = 1;
  for(const num of nums) {
    total = total * num;
  }
  return total;
}

console.log(multiply(1,2,3,4,5));
```

Will output:

```
120
```

##### **Functions**

Now in ES6 we have a new type of function called an _arrow function_. An _arrow function_ is shorthand for calling a new function, and is mostly the same. A major difference is that these two types of functions use different this parameters. To illustrate, first see a function declared normally:

```
const firstThree = ['Joseph', 'Rachel', 'Melody'].map(function(name) {
   return name.slice(0,3);
});

console.log(firstThree);
```

And the following is the same function but using an _arrow function_:

```
const firstThree = ['Joseph', 'Rachel', 'Melody'].map(
  name => name.slice(0,3)
);

console.log(firstThree);
```

They both output:

```
["Jos", "Rac", "Mel"]
```

The this parameter for each of them is different. If the object was called with new then this would be that object. Otherwise, this depends on the context of the method and if it was used with call() or apply(). So in a normal function, this is determined by _how_ the function is called. But for _arrow functions_, this is determined by the context of _where_ the function is called.

We can also use _default function parameters_ to provide fallback values for our function, allowing it to be called in a wider variety of situations. For example:

```
function eat(food = 'salad', size = 'medium') {
  return `Please enjoy your ${size} ${food}.`;
}

console.log(eat());
console.log(eat('steak', 'large'));
console.log(eat('steak'));
console.log(eat(undefined, 'large'));
```

Will output:

```
Please enjoy your medium salad.
Please enjoy your large steak.
Please enjoy your medium steak.
Please enjoy your large salad.
```

But if you want to set the size of the meal but keep the default food, you have to pass undefined through the function’s first argument when using an array of arguments like this. Instead of taking this tedious approach, we can combine _default function parameters_ with _destructuring_ to make powerfully flexible functions. If we pass an object with properties to the function, we can destructure the object’s properties inside the function, and provide defaults for any that might be missing. Since they’re matched by property name, they can be inserted in any order or in any combination. You can even provide a default object as an argument for the function to allow it to be called with just () with default values. For example:

```
function buildShip({sails = 4, size = 'medium', purpose = 'trade', coffers = 500, captain = 'Saltybeard'} = {}) {
  return `You build a ${size} ship with ${sails} sails. You appoint ${captain} as the captain and fill the coffers with ${coffers} pewter coins. You tell the captain to pursue ${purpose}.`;
}

console.log(buildShip());
console.log(buildShip({sails : 5, purpose : 'war'}));
console.log(buildShip({captain : 'Peggy', coffers : 750}));
```

Will output:

```
You build a medium ship with 4 sails. You appoint Saltybeard as the captain and fill the coffers with 500 pewter coins. You tell the captain to pursue trade.
You build a medium ship with 5 sails. You appoint Saltybeard as the captain and fill the coffers with 500 pewter coins. You tell the captain to pursue war.
You build a medium ship with 4 sails. You appoint Peggy as the captain and fill the coffers with 750 pewter coins. You tell the captain to pursue trade.
```

In ES6 we also get _classes_ for the first time in JavaScript, which can be deceptive! These new _classes_ are still just regular functions in disguise. You can designate constructor functions in a class declaration, and nest functions and variables in a style similar to other languages at this level of abstraction. However, it’s important to note that this does not change the pre-existing functionality of JavaScript functions, and we still use functions to create objects. You can extend _classes_ with extends and you can use super as either an object or a function to interact with the a child function’s parent. The syntax is also slightly different from declaring normal objects or functions. To demonstrate all of this:

```
class Vehicle {
  constructor(wheels = 4, gasoline = 15, color = 'silver') {
    this.wheels = wheels;
    this.gasoline = gasoline;
    this.color = color;
  }

  drive() {
    if (this.gasoline > 0) {
      console.log(`You use some gas to drive your ${this.color} vehicle further.`);
      this.gasoline--;
      console.log(`You now have ${this.gasoline} gallons of gas left.`);
    }
    else {
      console.log('You are out of gas!');
    }
  }
}

class Motorcycle extends Vehicle {
  constructor(wheels = 2, gasoline = 8, color) {
    super(wheels, gasoline, color);
  }

  drive() {
    if (this.gasoline > 0) {
      // a motorcycle only consumes half a gallon per drive,
      // so we need to offset the parent function if there's
      // enough gas to drive
      this.gasoline += 0.5;
    }
    super.drive();
  }

  doWheelie() {
    console.log('You do a cool wheelie!');  }
}

const myCar = new Vehicle();
const myTruck = new Vehicle(6, 0, 'blue');
const myMotorcycle = new Motorcycle(undefined, undefined, 'yellow');

myCar.drive();
myTruck.drive();
myMotorcycle.doWheelie();
myMotorcycle.drive();
```

Will output:

```
You use some gas to drive your silver vehicle further.
You now have 14 gallons of gas left.
You are out of gas!
You do a cool wheelie!
You use some gas to drive your yellow vehicle further.
You now have 7.5 gallons of gas left.
```

##### **Built-ins**

We now have _symbols_ which are unique, immutable data types. To create a _symbol_ you use the Symbol() syntax and you can pass it an optional description, like Symbol('frog'). Descriptions do not assign value to the _symbol_, so two _symbols_ with identical descriptions do not equal each other. _Symbols_ become useful for situations like adding duplicate properties to objects. For example:

```
const lake = {
  [Symbol('frog')]: { sound: 'ribbit', diet: 'bugs' },
  [Symbol('snake')]: { sound: 'hiss', diet: 'children' },
  [Symbol('frog')]: { sound: 'ribbit', diet: 'bugs' }
}
```

This allows us to add two separate properties with the descriptor ‘frog’ by giving them unique references via _symbols_.

Now in ES6 we have the flexibility to define our iteration behavior. By adhering to the _iterable protocol_ you can define and customize the iteration behavior of objects. Any object that is iterable can use the _for…of_ loop. To implement the _iterable interface_, you can access the iterator method via the constant [Symbol.iterator], which is a function that returns an _iterator object_. The _iterator protocol_ can be defined by implementing the .next()function on this object.

_Sets_ are a new object that can enforce a collection of distinct entries, like a mathematical set. To contrast, const array = [0, 1, 3, 4, 4]; can have any amount or combination of data, and adding more like array.push(4);won’t raise any complaints. If you need to enforce uniqueness within the collection, you can use a _Set_. It’s important to note that _Sets_ are not indexed based and you cannot access the entries individually. You can iterator over entries, add new ones, delete them, and clear the collection.

By using a _Set_, you gain access to a new group of functions for working with the entries. You can use .size() instead of .length() to return the length of the _Set_, and it’s very easy to use .has() to search the _Set_ for an entry. It will return true if it found the value. You can use the .values() or .keys() functions interchangeably to return an _iterator object_ (like above) which you can iterate over via .next() or a simple _for…of_ loop. For example:

```
const planets = new Set(['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']);

for (const planet of planets) {
  console.log(planet);
}
```

Will output:

```
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
```

_Sets_ have a sibling called _WeakSets_ which are more or less the same, but have a few more rules. They can only contain objects, they cannot be iterated over, and they cannot be cleared. Objects are removed from a _WeakSet_ when they are garbage collected. When the object represented in the _WeakSet_ is set to null then the object will be garbage collected and removed from the _WeakSet_.

_Sets_ and _WeakSets_ have close cousins called _Map_ and _WeakMap_. A _Set_ is to an array as a _Map_ is to an object. _Maps_ contain key-value pairs, much like objects contain properties with values. The keys and values can be represented as objects or values indiscriminately. You can’t assign values to a _Map_ at declaration, they have to be added in with the .set(key, value)function. You can iterate over a _Map_ like a _Set_, as well. For example:

```
const wealth = new Map();

wealth.set('Acme', 31.68);
wealth.set('Weyland', 90.34);
wealth.set('Yutani', 17.26);
wealth.set('Ecorp', 23.25);

for (const company of wealth) {
  const [key, value] = company;
  console.log(key, value);
}
```

Will output:

```
Acme 31.68
Weyland 90.34
Yutani 17.26
Ecorp 23.25
```

_WeakMaps_ are just like _WeakSets_ and have the same restrictions and garbage collection features. The key argument in a _WeakMap’s_ .set(key, value) function must be an object.

JavaScript now has _Promises_ with ES6. A _Promise_ lets you execute asynchronous code and call a function upon completion (or failure). We can attach callback functions to the object returned by the _Promise_ via the .then() method. Tools like _Proxies_ let you create “middlemen” that stand between any object and handler. _Proxies_ are more powerful than getter and setter functions because you don’t have to know the properties beforehand. You can rig _Proxies_ with traps to trigger on certain events on certain objects, allowing the _Proxy_ to intercept the function.

Perhaps my favorite addition to the language, we now have _generators_ which allow us to control the flow of execution. _Generator_ functions can be paused mid-execution, and they’re indicated with an *, like so: function* getDoctors(){ ... } (note that the * can be surrounded by spaces, or smushed right between the two words, just as long as it’s there). You can use the .next() function on a _generator_ to continue the execution (which begins paused). To insert a breakpoint, use the yield syntax. A cool thing about _generators_ is you can send data back into the function via the .next()method and send it out by attaching it to yield. For example:

```
function* getEmployee() {
  const planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune'];   
  const order = [];
  for (const planet of planets) {
    order.push(yield planet);
   }
  return order;
}
const generatorExample = getEmployee();

let planet = generatorExample.next().value;
planet = generatorExample.next(`The first planet is ${planet}.`).value;
planet = generatorExample.next(`The second planet is ${planet}.`).value;
planet = generatorExample.next(`The third planet is ${planet}.`).value;
planet = generatorExample.next(`The fourth planet is ${planet}.`).value;
planet = generatorExample.next(`The fifth planet is ${planet}.`).value;
planet = generatorExample.next(`The sixth planet is ${planet}.`).value;
planet = generatorExample.next(`The sevent planet is ${planet}.`).value;

const solarSystem = generatorExample.next(`The eighth planet is ${planet}.`).value; solarSystem.join('\n');console.log(solarSystem);
```

Will output:

```
["The first planet is Mercury.", "The second planet is Venus.", "The third planet is Earth.", "The fourth planet is Mars.", "The fifth planet is Jupiter.", "The sixth planet is Saturn.", "The seventh planet is Uranus.", "The eighth planet is Neptune."]
```

##### **Transpiling**

To take advantage of these new ES6 features on the web, they have to be supported by the user’s browser. You can check out this [browser compatibility table](https://kangax.github.io/compat-table/es6/) to see what ES6 features are supported on what browser versions. I’ve also included links to each major browser’s platform status in the resources at the end of this article. If a feature you use is not supported by a target browser, you have to _transpile_ your code into ES5 prior to deploying.

According to the [W3 Global Web Stats](https://www.w3counter.com/globalstats.php), less than 4% of web surfer still use IE11 (which fails most ES6 checks). However, new bleeding edge JavaScript features come out all the time, and to use them in a deployment environment for most users, you will want to consider transpiling your code with a tool like [Babel](https://babeljs.io/) for most browsers.

Thankfully in 2018, most ES6 features are supported by most modern browsers, so this isn’t always a necessity. You can also target specific functionality via _polyfills_, which stand in for newer code. [Modernizr has collected a list of polyfills](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills), and I recommend checking them out.

I’ve just scratched the surface, so I recommend checking out the [full list of ES6 features](http://es6-features.org/) to learn more about what’s supported. Now that I’ve finished all of the lesson modules, I have effectively finished my challenge course for Google and Udacity. At this point, I have to wait until April 11th for the program to end. Announcements for who made the top 1,000 (and win a free Nanodegree program) will come out on April 17th. I plan to spend my time between now and then reflecting on these lessons, completing more Udacity courses, and applying what I’ve learned towards some new projects.

[Click here to read Part 4!]()

#### **Resources**

[ECMAScript 6 Features](http://es6-features.org/)

[ECMAScript 6 Browser Compatibility Table](https://kangax.github.io/compat-table/es6/)

[Babel](https://babeljs.io/)

[**Chrome** Platform Status (for ES6)](https://www.chromestatus.com/features#ES6)

[**Edge** Platform Status (for ES6)](https://developer.microsoft.com/en-us/microsoft-edge/platform/status/?q=ES6)

[**Firefox** Platform Status (for ES6)](https://platform-status.mozilla.org/)

[**WebKit (i.e. Safari)** Feature Status (for ES6)](https://webkit.org/status/)

**Remy Sharp:** [What is a Polyfill?](https://remysharp.com/2010/10/08/what-is-a-polyfill)

**Modernizr:** [HTML5 Cross Browser Polyfills](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills)

**[Ecma International](http://www.ecma-international.org/):** [ECMAScript 2015 Language Specification](http://www.ecma-international.org/ecma-262/6.0/index.html)

**Udacity/Google:** [ES6 - JavaScript Improved](https://www.udacity.com/course/es6-javascript-improved--ud356)
