# ES2015 New Features

## Useful Links
- [Babel Repl](https://babeljs.io/repl/)
- [Traceur Repl](https://google.github.io/traceur-compiler/demo/repl.html)
- [ECMAScript Compatibility Table](http://kangax.github.io/compat-table/es6/)
- [Babel Learn ES2015](https://babeljs.io/docs/learn-es2015/)
- [PonyFoo ES6 Articles](https://ponyfoo.com/articles/tagged/es6)



## Proper tail calls

- Only work in Strict mode
- Optimize code so that Tail Position is a Tail Call

```javascript
// How many times recursion can be called
function foo(num) {
  try {
    return foo( (num || 0) + 1 );
  } catch(e) {
    return num;
  }
}
console.log(foo());
```

```javascript
// Fibonacci with proper tails calls
function fibonacci(x, y, limit, index) {
  if(arguments.length == 1) {
    if(x) {
      return fibonacci(0, 1, x, 1);
    }
    return 0;
  } else {
    if(index < limit) {
      return fibonacci(y, (x+y), limit, ++index);
    }
    return y;
  }
}
console.log(fibonacci(3));
console.log(fibonacci(10));
console.log(fibonacci(12495));
```


## Let, Const, Block

- Variable hoisting.
- `let` uses block scope, the curly braces are the new scope.
- Can't dountle declare a variable with `let`.
- Temporal Dead Zone, no hoisting - throws an error if used before defined.
- `const` variable cannot be reassigned, scoped like `let`.
- Block functions declare the same lexical scope as an if-true

```javascript
let foo = 2;
if (true) {
  let bar = 2;
}
console.log(bar); // Throws error
```

```javascript
for(var j = 0; j < 10; j++) {
  console.log(j); // logs 0-9
}
console.log(j); // ReferenceError
```

```javascript
const a = 0;
a = 1; // SyntaxError: Assignment to constant variable
```

```javascript
function doTihng() {
  // some code
  { // lay down a new scope
    let a = 0;
  }
}
```


## Rest Parameters

- Same ideas as splats
- Catches all parameters in an array, a proper array
- Includes only non-specified arguments, unlike `arguments`
- Rules of Rest Parameters
  - One per function
  - Must be the last parameter
  - Can't use `arguments`
  - No Default values


```javascript
function myFunction(first, last, ...other) {
  // code
  console.log(other.joint(' '));
  
}
function myFunction('I', 'can', 'haz', 'teh', 'arguments');
```


## Spread Operator

- `...` before an array

```javascript
var nums = [1, 2, 3];

console.log(nums);    // [1, 2, 3]
console.log(..nums);  // 1, 2, 3
```

```javascript
let nums = [1, 2, 3];
let abcs = ['a', 'b', 'c'];

let alphanum = [...nums, ...abcs];
```


## Destructuring

- New syntax to deconstruct Objects and Arrays
- Pattern matching
- Braces on the lest side on an assignment expression
- Creates separate variables
- Can alias the names using a colon
- Can be as a parameter to a function
- Can receive default values
- `}` on the left mean destructure the right
- By default, properties that aren’t found will be undefined, just like when accessing properties on an object with the dot or bracket notation.
- If you’re trying to access a deeply nested property of a parent that doesn’t exist, then you’ll get an exception, though.
- Destructuring can nest


```javascript
let {city, state, zip} = getAddress();

// Aliasing the properties names
let {city: c, state: s, zip: z} = getAddress();
```

```javascript
var person = {name: 'Aaron', age: 35};
displayPerson(person);

function displayPerson({name = "No Name Provided", age = 0}) {
  // destructures the parameters
  // do something
}
```

```javascript
let {a: x} = {};  // undefined
let {a: x = 1} = {} // 1
let {name, age, address: {city, state, zip}} = person;
```

## Destructuring Arrays

- Similar to destructuring objects

```javascript
var nums = [1,2,3,4,5,6,7,8,9,10];

var [first, second,,,,,,,, tenth] = nums;
console.log(first, second, tenth);
```

```javascript
// swap variables
[b, a] = [a, b]:
```

```javascript
// as part of the method signature
var nums = [1, 2, 3, 4];
doSomething(nums);

function doSomething([first, second, ...others]) {
  console.log(first);
  console.log(second);
  console.log(others);
}
```

```javascript
// nested arrays
var nums = [1, 2, [3, 4, [5, 6]]];
var [one,,[three,,[,six]]] = nums;
```

```javascript
// pattern errors
let [x] = [2, 3];        // x = 2
let [x] = {'0': 4};      // throw
let [x, y, z] = [1, 2];  // z = undefined
```


## Arrow Functions

- Like lambdas in Ruby or Fat arrow functions from CoffeeScript
- Parentheses for the parameters
- No braces for single-line arrow function bodies
- Single-line arrow, implicit return statement
- Lexical binding of `this`
- Can't alter `this` on arrow function, even if used with `call`.
- Still use functions

```javascript
var fn1 = function() {return 2;};
var fn2 = () => 2;
```

```javascript
// parameter rules
var x;
x = () => {};      // No parameters, MUST HAVE PARENS
x = (val) => {};   // One parameter w/ parens , OPTIONAL
x = val => {};     // One parameter w/o parens, OPTIONAL
x = (y, z) => {};  // Two or more parameters, MUST HAVE PARENS
x = y, z => {};    // Syntax Error, must wrap with parens
```

```javascript
// body rules
var square;
square = x => x * x;            // Body w/o braces
square = x => { return x * x};  // Body w/ braces
```

```javascript
// use to replace anonymous functions
let nums = [1, 2, 3];
let res = nums.map( n => n * n );
```

```javascript
let key_maker = val => ({key: val});
console.log(key_maker(100));   // Logs {key: 100}
```

```javascript
var Widget = {
  init: function() {
    // The arrow function binds this to the object
    document.addEventListener("click", (event) => {
      this.doSomething(event.type);
    }, false);
  },
  doSomething: function(type) {
    console.log("Handling " + type + " event");
  }
};
Widget.init();
```

```javascript
var f = () => {};
typeof f;   // function
Object.getPrototypeOf(f);   // [Function:f]
new f();    // TypeError: f is not a constructor
```

```javascript
function Widget() {
  this.id = 123;
  // log is defined using the arrow function, this is bound lexically
  this.log = () => {
    console.log('Widget Log', this.id);
  }
}

var pseudoWidget = { id: 345 };
// call will still use Widget() for this
new Widget().log.call(pseudoWidget);   // Widget Log 123
```


## Default Parameters
- Used to help in data-proofing tasks
- `undefined` will trigger the default assignment
- The default value can be assigned to a function call
- Assignment happens lexically
- Not all parameters need default values
- No default parameters with rest parameters
- Default values don't appear in `arguments`

```javascript
function sayHello(name = "World") {
  console.log("Hello " + name + "!");
}

sayHello("Vadim");     // Hello Vadim!
sayHello("");          // Hello  !
sayHello();            // Hello World!
sayHello(undefined);   // Hello World!
```

```javascript
function myFunction(id = getRand()) {
  // code
}
```

```javascript
function die() { throw "x" }

function test(a = die()) {
  console.log("Didn't die");
}

text();   // throws an error
```

```javascript
var x = "INIT";

// x is scoped lecially within the function
function test(a = x) {
  var x;  // x is undefined
  return a;
}

test();  // undefined
```

```javascript
function test(a = 1, b = 2, c = 3) {
  console.log(arguments.length);
}

test();               // 0
test(1);              // 1
test(1, 2, 3, 4, 5);  // 5
```


## Classes

- The class system is only syntactic sugar over things that are already in the language
- Allows for constructors
- Can have private properties via `Symbol()`
- Have Setter and Getter properties
- Can have class properties
- Classes can be extended, using the `extend` and `super` keywords
- Classes do not hoist
- If constructor is missing it will do the default behavior, call constructor of the super class

```javascript
function Foo() {
  // code
}

// same as
class Foo {
  // code
}
```

```javascript
// Symbol is used to generate an obscure property name
var monsterHealth = Symbol();

class Monster {
  // classes have constructors
  constructor(name, health) {
    this.name = name;
    this[monsterHealth] = health;

    // Class Property
    Monster.allMonsters.push(this);
  }

  // getter property
  get isAlive() {
    return this[monsterHealth] > 0;
  }

  // setter property
  set isAlive(alive) {
    if (!alive) {
      this[monsterHealth] = 0;
    }
  }
}

var kevin = new Monster('Kevin', 100);
kevin.isAlive;          // used getter -> true
kevin.isAlive = false;  // used setter
kevin.isAlive;          // false

Monster.allMonsters = [];
```

```javascript
class Monster {
  constructor(name, health) {
    this.name = name;
    this[monsterHealth] = health;
  }
  // code
}

class Godzilla extends Monster {
  constructor() {
    super('Godzilla', 10000);
  }
}
```

```javascript
// extends can use any function
class MySocket extends getClass() {
  // code
}

function getClass() {
  if(isIE()) {
    return IEWebSocketImpl;
  }
  return WebSocket;

  function isIE() {
    return false;
  }
}
```


## Collections

- Three new collections: `set`, `map`, `weakmap`
- `set` is like `Array`
  - unique collection of things
  - no typecasting on uniqueness
  - items are enumerable
- `map` is like key-value `Object`
  - no typecasting on key
  - objects can acts as keys
  - Keys: primitives / objects / functions
  - Must use the same key, not equal key
  - Map has `.entries()` method to interate over contents
  - Map is aware of `.size`
  - Can cause memory leaks if used to store DOM elements
- `weakmap` is like map, but different
  - Very similar to a `map`
  - Does not have a size
  - Stores a weak element to the DOM element, can solve the memory leak problem
  - Does not prevent garbage collection


```javascript
var set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.size;    // 3
```

```javascript
var set = new Set();
set.has(1);     // false
set.add(1);
set.has(1);     // true, check for inclusion
set.clear();    // to clear the set
set.has(1);     // false
set.add(1);
set.add(2);
set.size;       // 2
set.delete(2);  // delete an item
set.size;       // 1
```

```javascript
var items = new Set([1,2,3,4,5]);

for (let num of items) {
  console.log(num);
}
```

```javascript
// map
var json = { name: "Aaron" };

var map = new Map();

// map.set method
map.set('name', 'Aaron');

// map.get method
map.get('name');   // Aaron
```

```javascript
var user = { name: 'Aaron', id: 1234 },
var userHobbyMap = new Map();

// complex object as the key of a map
userHobbyMap.set(user, ['Fishing', 'Hiking']);
userHobbyMap.get(user);
```

```javascript
var user = { name: 'Aaron', id: 1234 };
var weak = new WeakMap();

weak.has(user);    // false
weak.set(user, {lastAction: newDate()});
weak.has(user);    // true
weak.delete(user);
weak.has(user);    // false
weak.has(user);    // true
weak.set(user, {lastAction: newDate()});
weak.clear();
weak.has(user);    // true
```


## Promises

- Solves the problem of nested callback structure for asyc code
- Good for Ajax, WebSockets, Read / Write localStorage, Write lots to DOM, Show a spinner for loading etc.
- Promises constructors have 2 methods: `resolve`, `reject`
- Promise instances can be in 1 of 4 states:
  - fulfilled - successfully resolved - `1`
  - rejected - `2`
  - pending - hasn't resolved or rejected yet - `undefined`
  - settled - fulfilled or rejected - `1` or `2`
- Promise `then` function is not executed until the promise resolved
- Static Promise Methods:
  - `Promise.all(iterable);` - wait until all settle
  - `Promise.race(iterable);` - wait until 1 settles
  - `Promise.reject(reason);` - create a promise that is already rejected
  - `Promise.resolve(reason);` - create a promise that is already resolved

```javascript
// Promise constructor
var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then...

  if (/* everything is ok */) {
    resolve('Stuff worked!');
  } else {
    reject(Error('It broke'));
  }
});

return promise;  // Give this to someone
```

```javascript
// Promise instance
promise.then(function(result){
  console.log(result);  // Stuff worked!
}, function(err) {
  console.log(err);     // Error: it broke
});
```

```javascript
// ajax example, get method with promises
function get(url) {
  return new Promise(function(resolve, reject){
    $.get(url, function(data){
      resolve(data);
    })
    .fail(function() {
      reject();
    });
  });
}

// usage
get('users.all').then(function(users){
  myController.users = users;
}, function(){
  delete myController.users;
});

// or .catch instead of second handler in .then
get('users.all')
  .then(function(users){
    myController.users = users;
  })
  .catch(function(){
    delete myController.users;
  });
```

```javascript
// combine promises
var usersPromise = get('users.all');
var postsPromise = get('posts.everyone');

// wait until both are settled
Promise.all([usersPromise, postsPromise])
.then(function(results){
  myController.users = results[0];
  myController.posts = results[1];
}, function() {
  delete myController.users;
  delete myController.posts;
});
```

```javascript
// If the response in a string, can use additional .then
get('users.all').then(function(userString){
  return JSON.parse(userString);
}).then(function(users){
  myController.users = users;
});

// even more compact
get('users.all').then(JSON.parse).then(function(users){
  myController.users = users;
});
```


## Generators

- Enable JS to be more collaborative on long running processes
- Help to remove blocking operations
- Has a `*` either on the function name or at the end of the `function*` keyword
- Uses the `yield` keyword, kinda like `return`, can return or get information
- `for in` loops continue while `done: false`

```javascript
// basic syntax, * before the name of the function
function *myGen() {
  // ...
  yield 1;
  yield 1;
  return 3;
}

// or after the function
function* myGen() {
  // ...
  yield 1;
  yield 1;
  return 3;
}
```

```javascript
function *three() {
  yield 1;
  yield 1;
  return 3;
}

// Calling `three()` does not execute the function
// It returns a generator iterator
var geni = three();

// Starts the excution
geni.next();  // Return { value: 1, done: false }
geni.next();  // Return { value: 2, done: false }
geni.next();  // Return { value: 3, done: true }
geni.next();  // Return { value: undefined, done: true }
```

```javascript
function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1, 2, 3, 4, 5
// 6 does not get logged, because done: true
```

```javascript
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield(y / 3);
  return (x + y + z);
}

var genit = foo(5);

genit.next();         // {value: 6, done: false}
genit.next(12);       // {value: 8, done: false}
genit.next(13);       // {value: 42, done: true}
```