Notes on ECMAScript 6 (ES6)
Tags: javascript, es6, ecmascript
Published: 2018-08-22 01:36:00

---
title: Notes on ECMAScript 6 (ES6)
published: true
description: 
tags: JavaScript, ES6, ECMAScript
---

## Introduction

_This is not meant to replace the official documentation_.

This post does not cover all the ES6 features.

For typos and corrections: https://github.com/hardy613/es6-notes/issues


## ES6 Variables

### `var` vs `let`
Traditionally the keyword `var` initializes the `identifier` with a `value`:

```js
var my_variable	= 'value';
//1 //2		    //3 

//1 the var keyword
//2 the identifier
//3 the value
```
There are rules for naming the variable identifier. These are:
- identifiers cannot be keywords
- can be alphanumeric, although cannot start with a number
- `$` and `_` are also allowed characters for an identifier

Variables decalred by `var` have the scope of the entire function.

```js
function myFunc() {
	if(true) {
		var my_var = 'test';
	}
	console.log(my_var); // test
}
```

### The `let` keyword

`let` is preferred over `var`. Variables decalred by `let` have their scope
within the `block` they are defined.

```js
function myFunc() {
	if(true) {
		let my_var = 'test';
	}
	console.log(my_var); // TypeError
}
```

Block scoping allows for variable [`shadowing`](https://en.wikipedia.org/wiki/Variable_shadowing).

```js
function myFunc() {
	let my_var = 'test';
	if(true) {
		let my_var = 'new test';
		console.log(my_var); // new test
	}
	console.log(my_var); // test
}
```

### The `const` keyword

ES6 also introduced a new variable keyword: `const`. Variables declared with 
the `const` keyword are block scoped just like `let` however they cannot 
change by reassignment and they cannot be re-declared; they are immutable.

```js
const version = '0.0.1';
version = '0.0.2'; // TypeError: invalid assignment to const

const name = 'bill';
const name = 'ted'; // SyntaxError: Identifier 'name' has already been declared
```
Variables declared by `const` (constants) cannot be changed. However, with a 
for loop the scope is redeclared at the start of each loop, where a new 
`const` can be initalized.

```js 

function myFunc(items) {
	for(let i = 0; i < items.length; i++) {
		const message = items[i] + ' found at index: ' + i;
		console.log(message);
	} 
}

myFunc(['test', 100, 200]);
// test found at index: 0
// 100 found at index: 1
// 200 found at index: 2
```

## ES6 for/of

The `for/of` loop uses the [iterable protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol) to create a loop. Strings, Arrays, TypedArray, Map, Set, NodeList, and custom iterable function hooks can all be used with `for/of`.

```js
const arr = [1, 2, 3];
for(const number of arr) {
	console.log(number) // 1 2 3
}
```

To iterate over an object you can use the protocol `Object.entries()`.
This will give arrays of `['key', 'value']` pairs. Unlike `for/in` this will
not iterate through the object prototype

```js
const obj = { a:1, b:2, c:3 };
for(const prop of Object.entries(obj)) {
	console.log(prop); // ['a', 1] ['b', 2] ['c', 3]
}
```

## ES6 Template Literals

Template literals are very handy for strings that use variables, or need to 
make use of a quick javascript expression. Template literals are enclosed with 
the back-tick. Template literals can also have `placeholders`, 
these are declared with a dollar sign and curly braces `${placeholder}`.
```js
const number = 42;
const str = `Here's my favourite number: ${number}.`;
console.log(str) // Here's my favourite number: 42.

const count = 0;
console.log(`${count + 1}`); // 1 
```

Template literals can be `tagged` with a function identifier before the 
back-ticks. The function allows you to parse the template literal. The first 
argument is an array of string values, the rest of the arguments relate to 
the placeholders in the template literal.

```js
const name = 'Theodor Logan';
const age = 21;

function showNameAndAge(strings, nameHolder, ageHolder) {
    // strings[0] is empty because we started with a
    // ${name} placeholder, placeholders at the start or 
    // at the end of a template literal will have
    // an empty string before or after respectively 
	const piece1 = strings[1]; // is
	const piece2 = strings[2]; // years of age.
	let ageNotice = '';
	if(ageHolder < 25) {
		ageNotice = 'What a babyface. ';
	} else {
		ageNotice = 'What an oldtimer. ';
	}
	return `${ageNotice}${nameHolder}${piece1}${ageHolder}${piece2}`;
}

showNameAndAge`${name} is ${age} years of age.` 
// What a babyface. Theodor Loagn is 21 years of age.
```

Tagged templates literals do not need to return a string.

## ES6 Arrow Functions

Arrow functions are a shorthand syntax for functions that do not contain its 
own `this`, `arguments`, `super`, or `new.target` and cannot be used as 
constructors.

```js
const arr = ['hammer', 'nails', 'pizza', 'test'];
console.log(arr.map(value => value.length)); // [6, 5, 5, 4]
```

Arrow functions are useful for anonymous functions,
however their power is with the lexical scoping of `this`.

```js
function es6LexicalScope() {
	this.timeSpentSeconds = 0;
	setInterval(() => {
		console.log(this.timeSpentSeconds++); // 1 2 3 ...
	}, 1000);
}
es6LexicalScope();
```

Arrow functions do not have a `prototype`.

```js
const func = () => {};
console.log(func.prototype); // undefined
```

To return an object as an implicit return, you can wrap the object in 
the [`grouping operator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Grouping) (parentheses).

```js
const returnObjBad = () => { test: 'value' };
console.log(returnObj); // undefined

const returnObjGood = () => ({test: 'value'});
console.log(returnObj); // { test: 'value' }
```

If you noticed, there is a small difference between the usage of arrow 
functions in the provided exmaples. The usage of `()`:
- Arrow functions with no parameters require `()`
- Arrow functions with one parmeter `()` are optional
- Arrow functions with two or more parameters require `()`
- Arrow functions that only return, do not need `{}`, `return`, or `;`

```js
const fn1 = () => {[Native Code]};
const fn2 = param => {[Native Code]};
const fn2a = (param) => {[Native Code]};
const fn3 = (param1, param2) => {[Native Code]};
const fn4 = param => param;
```
## ES6 Destructuring Assignment

Destructuring assignment lets you unpack values from an array or object.

```js
const [x, y] = [1, 2, 3, 4, 5];
console.log(x); // 1
console.log(y); // 2;

const person = { name: 'Bill', age: 42, email: 'bill@example.ca', url: 'http://example.ca' };
const {name, age} = person;
console.log(name, age); // Bill, 42
```

Sometimes you want to keep all the other stuff. That is where the spread 
operator [`...`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) comes in handy.

```js
const [x, y, ...allTheRest] = [1, 2, 3, 4, 5];
console.log(x, y, allTheRest); // 1, 2, [3, 4, 5]

const person = { name: 'Bill', age: 42, email: 'bill@example.ca', url: 'http://example.ca' };
const {name, age, ...details} = person;
console.log(name, age, details); // Bill, 42, {email: 'bill@example.ca', url: 'http://example.ca'}
```

You can also destructure to build new variables!

```js
const otherObj = {};
const person = { name: 'Bill', age: 42, email: 'bill@example.ca', url: 'http://example.ca' };
const obj = {...otherObj, person};
console.log(obj); // { person: {[...]} }
```

`obj` now has our `person` property with our person `Bill`. If the person 
property was already set in `otherObj` then we would override that property. 
Let's look at unpacking the length property from a string with destructuring.


```js
const arr = ['hammer', 'nails', 'pizza', 'test'];
// without destructuring
console.log(arr.map(value => value.length)); // [6, 5, 5, 4]
// with destructuring
console.log(arr.map(({ length }) => length)); // [6, 5, 5, 4]
```

Let's breakdown the line we just added. `console.log(arr.map(` is pretty 
standard. `({ length })` is the parameter for our arrow function, we are passing 
in a string and destructuring the length property from the string and passing 
that as a variable called `length`. The function parameter is the string 
length. `=> length));` the rest of our arrow function. The property is also 
the variable identifier and we only return the `length`. If you need a default 
with destructuring, you can do that too!

```js 
const { name = 'Bill', age = 30 } = { name: 'Ted' };
console.log(name, age)// Ted, 30

const [x = 5, y = 10] = [20];
console.log(x, y) // 20, 10
```
## ES6 Default Parameters

Funtions accept default parameters and destructuring parameters.

```js
function addToFive(addTo = 0) {
	return addTo + 5;	
}
const ex1 = addToFive();
const ex2 = addToFive(5);
console.log(ex1, ex2); // 5, 10

function fullname ({firstname, lastname}) {
	return `${firstname lastname}`;
}
const user = { firstname: 'Theodore', lastname: 'Logan', age: '20' };
const fullname = fullname(user);
console.log(`Hello ${fullname}`);
```

When destructuring you can also assign defaults.

```js
function myFunc({age = 42}) {
	console.log(age); // 42
};
myFunc({name: 'Theodor'});
```
## ES6 Classes

ES6 `class` is new syntax for the traditional classes introduced in ES2015. 
ES6 Classes are not introducing anything to JavaScript rather _just another way 
to write a JavaScript class_. Class bodys are subject to JavaScript's
`strict mode`, the class body has new keywords and some words are 
reserved as keywords for future use.

As with functions there are two ways to declare a class, `expression` or 
`declaration`. 

```js
// expression
const Instrument = class {}; // or class Instrument {}
const instrument = new Instrument();

// declaration
class Instrument {}
const instrument = new Instrument();
```

Unlike a function, a class must be declared or expressed before it can used.

### Constructors

`constructor` is a reserved keyword for classes and represent a function that
is called during the creation and initialization.

```js
class Instrument {
	constructor(props) {
		this._make = props.make;
		this._type = props.type;
	}

	get type() {
		return this._type;
	}
}

const noiseMaker = new Instrument({ make: 'Crafter', type: 'Guitar' });
console.log(noiseMaker.type); // Guitar
```
### Getters and Setters

`getters` and `setters` allow read and write access to class properties without 
having to define methods. Getters and setters are accessible by inherited 
classes.

```js
class Instrument {
	constructor(props) {
		this._make = props.make;
		this._type = props.type;
	}

	set make(make) {
		this._make = make;
	}

	get make() {
		return this._make;
	}

	set type(type) {
	 this._type = type;
	}

	get type() {
		return this._type;
	}

}

const noiseMaker = new Instrument({ make: 'Crafter', type: 'Guitar' });
noiseMaker.type = 'Drums';
noiseMaker.make = 'Yamaha';
console.log(noiseMaker.type); // Drums
```

### Inheriting

Classes can inherit a parent class. Keeping with Instruments, let's make a 
guitar class. The `super` keyword refers to the class being inherited.

```js
class Guitar extends Instrument {
	constructor(make) {
		super({make, type: 'Guitar'});
	}
	set make (make) {
		super.make = make
	}
	get make() {
		return `The make of the guitar is: ${super.make}`;
	}
}

const myGuitar = new Guitar('Fender');
console.log(myGuitar.make); // The make of the guitar is: Fender
myGuitar.make = 'Crafter';
console.log(myGuitar.make); // The make of the guitar is: Crafter
console.log(myGuitar.type); // Guitar
```

### Methods

Class methods are functions with the `function` keyword dropped.

```js
class Guitar extends Instrument {
	constructor(make) {
		super({make, type: 'Guitar'});
	}

	set make (make) {
		super.make = make
	}

	get make() {
		return `The make of the guitar is: ${super.make}`;
	}

	log() {
		console.log(this.make, this.type);
	}
}

const fender = new Guitar('Fender');
fender.log(); // The make of this guitar is: Fender, Guitar
```

### Object Definitions

Currently our object `.toString()` definition would return `[object Object]`.
We can change the definition with a method property.

```js
class Guitar extends Instrument {
	constructor(make) {
		super({make, type: 'Guitar'});
	}

	set make (make) {
		super.make = make
	}

	get make() {
		return `The make of the guitar is: ${super.make}`;
	}

	toString() {
		return `[${super.name} ${this.type}]`;
	}
}

const fender = new Guitar('Fender');
console.log(fender.toString()); // [Instrument Guitar]
```

### `super` and `this`

Before you can use `this.property` in a constructor of an inherited class, you 
must call `super()` first.

```js
class Guitar extends Instrument {
	constructor(make, stringCount) {
		super({make, type: 'Guitar'});
		this._stringCount = stringCount || 6;
	}

	set make (make) {
		super.make = make
	}

	get make() {
		return `The make of the guitar is: ${super.make}`;
	}

	get stringCount() {
		return this._stringCount;
	}

	set stringCount(stringCount) {
		this._stringCount = stringCount;
	}
}

const guitar = new Guitar('Fender', 12);
console.log(guitar.stringCount); // 12
```
## ES6 Modules

ES6 modules use the `import` and `export` keywords and are intended to be used
with the browser or with a server environment like `NodeJs`

```js
// utils.js
export function add(left = 0, right = 0) {
	return left + right;	
};

export function times(left = 0, right = 0) {
	return left * right;
}
```

Now we can import our utils file. There are a few ways we can import.

```js
// index.js
import * as utils from './utils.js'
// utils.add(), utils.times()

import { add, times } from './utils.js'
// add(), times()
```

You can also export variables or objects. 

```js
// my-module.js

const myVariable = 100;

const person = {
	name: 'Bill',
	age: 42
};

function trim(string = '') {
	return typeof string === 'string' && string.trim();
};

export { myVariable, person, trim };

// index.js
import { myVariable as maxAge, person, trim } from './my-module.js';

console.log(maxAge, person.age); // 100, 42

trim(' test '); // 'test'
```

There are two different types of export, `named` and `default`. You can have 
multiple `named` exports in a module but only one `default` export. The above 
examples are all from the `named` export, let's take a look at the `default` 
export syntax.

```js
// a default funtion
export default function() {[...]}
export default function myFunc() {[...]}

// a default class
export default class MyClass {[...]}
```

You can also have a variable as a default export

```js 
// other-module.js
const mySuperLongNamedVariable = 100;
export default mySuperLongNamedVariable;
```

When importing defaults you can name them without the ` * as ` keyword.

```js
// index.js
import theVariable from './other-module.js'
console.log(theVariable); // 100
```
## ES6 Promises

> A Promise is an object representing the eventual completion or failure of an 
> asynchronous operation.
> - [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)

### Working with promises

Promises are a convenient way to organize the order of operation for your 
program and provide and alternative to passing callbacks as function parameters.
Say we have a function `callToDb` that makes a database call and returns a 
promise

```js
function success(result) {
	// do something with result
}

function failed(error) {
	// do something with error
}

callToDb('table_name').then(success, failed);
```

`failed` is only called if an `Error` is returned. Both of these arguments are 
optional, however to use the result of the previous promise you need at least 
a success function with one argument

```js

callToDb('table_name')
	.then(response => {
		// do something with response
	})
	.catch(error => {
		// do something with error
	});
```

Like the above `failed` function, `catch` is only called if an `Error` is 
returned. `then` returns a promise meaning we can now create a promise `chain`.

```js

callToDb('table_name')
	.then(response => {
		// do something with response
		response.changesMade = true;
		return response;
	})
	.then(response => {
		// do more work
	})
	.catch(error => {
		// do something with error
	});
```

Chains can be as long as you need them. `catch` can also be used multiple 
times in a promise chain, the next `catch` in the chain is called on return 
of an `Error` and following `then`s will still be called.

```js

callToDb('table_name')
	.then(response => {
		// do something with response
		response.changesMade = true;
		return response;
	})
	.then(response => {
		// do more work
	})
	.catch(error => {
		// only called for above thens
	})
	.then(response => {
		// do more work
		// will still happen after the catch, even if catch is called
	})
	.catch(error => {
		// do something with error
		// only called for the one above then if an Error is returned
	});
```

### Creating a promise

The promise constructor should only be used to to wrap a function that does not 
support a promise. Most libraries have built-in support for promises which 
enable you to start chaining `then` right out of the box without a promise 
constructor.

The promise constructor takes one `executor` function with two arguments: 
`resolve` and `reject`. Let's create `callToDb`, a wrapping function to a 
function without promise support.

```js

function callToDb(table_name) {
	return new Promise((resolve, reject) => {
		return db_orm(`select * from ${table_name}`, (err, res) => {
			if(err) {
				reject(err);
			} else {
				resolve(res);
			}
		})
	});
}
```
A few things are happening here:

- `db_orm` is our database library without promise support, it takes a callback
- wrapping `db_orm` is our returning `Promise` which has our executor function
with `resolve` and `reject`
- once `db_orm` is in the callback we reject with the error, 
this will trigger a `catch` or
- we `resolve` with our result, this will trigger the next `then`

### Reject

Reject returns a promise that is rejected with a `reason`. To debug with ease 
it is recommended to make the `reason` an `instance of Error`

```js
Promise.reject(new Error('My custom message'))
	.then(result => {
		// not called
	})
	.catch(result => {
		console.log(result); // Error: My custom message
	})
```
To reject a promise inside a `then` chain you can return a `new Error` or 
throw an `Error` to the catch.

### Resolve

Resolve returns a promise that is resolved with a `result`. `result` can also 
be another `promise`, `thenable` or value.

```js
Promise.resolve('Sweet!')
	.then(result => {
		console.log(res); // Sweet!
	})
	.catch(result => {
		// not called
	});
```
Thanks to Hannah and Jonathan for helping proof read and thank you for reading.

I hope this helps!

edits: To code blocks

Cheers.