# 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} ```