Skip to content

Instantly share code, notes, and snippets.

@VadimBrodsky
Last active September 24, 2016 18:33
Show Gist options
  • Save VadimBrodsky/d0d9771f9ccf3a6c2517fece77e6000b to your computer and use it in GitHub Desktop.
Save VadimBrodsky/d0d9771f9ccf3a6c2517fece77e6000b to your computer and use it in GitHub Desktop.
ES6 - FrontEnd Masters Notes

ES2015 New Features

Useful Links

Proper tail calls

  • Only work in Strict mode
  • Optimize code so that Tail Position is a Tail Call
// How many times recursion can be called
function foo(num) {
  try {
    return foo( (num || 0) + 1 );
  } catch(e) {
    return num;
  }
}
console.log(foo());
// 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
let foo = 2;
if (true) {
  let bar = 2;
}
console.log(bar); // Throws error
for(var j = 0; j < 10; j++) {
  console.log(j); // logs 0-9
}
console.log(j); // ReferenceError
const a = 0;
a = 1; // SyntaxError: Assignment to constant variable
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
function myFunction(first, last, ...other) {
  // code
  console.log(other.joint(' '));
  
}
function myFunction('I', 'can', 'haz', 'teh', 'arguments');

Spread Operator

  • ... before an array
var nums = [1, 2, 3];

console.log(nums);    // [1, 2, 3]
console.log(..nums);  // 1, 2, 3
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
let {city, state, zip} = getAddress();

// Aliasing the properties names
let {city: c, state: s, zip: z} = getAddress();
var person = {name: 'Aaron', age: 35};
displayPerson(person);

function displayPerson({name = "No Name Provided", age = 0}) {
  // destructures the parameters
  // do something
}
let {a: x} = {};  // undefined
let {a: x = 1} = {} // 1
let {name, age, address: {city, state, zip}} = person;

Destructuring Arrays

  • Similar to destructuring objects
var nums = [1,2,3,4,5,6,7,8,9,10];

var [first, second,,,,,,,, tenth] = nums;
console.log(first, second, tenth);
// swap variables
[b, a] = [a, b]:
// 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);
}
// nested arrays
var nums = [1, 2, [3, 4, [5, 6]]];
var [one,,[three,,[,six]]] = nums;
// 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
var fn1 = function() {return 2;};
var fn2 = () => 2;
// 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
// body rules
var square;
square = x => x * x;            // Body w/o braces
square = x => { return x * x};  // Body w/ braces
// use to replace anonymous functions
let nums = [1, 2, 3];
let res = nums.map( n => n * n );
let key_maker = val => ({key: val});
console.log(key_maker(100));   // Logs {key: 100}
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();
var f = () => {};
typeof f;   // function
Object.getPrototypeOf(f);   // [Function:f]
new f();    // TypeError: f is not a constructor
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
function sayHello(name = "World") {
  console.log("Hello " + name + "!");
}

sayHello("Vadim");     // Hello Vadim!
sayHello("");          // Hello  !
sayHello();            // Hello World!
sayHello(undefined);   // Hello World!
function myFunction(id = getRand()) {
  // code
}
function die() { throw "x" }

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

text();   // throws an error
var x = "INIT";

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

test();  // undefined
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
function Foo() {
  // code
}

// same as
class Foo {
  // code
}
// 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 = [];
class Monster {
  constructor(name, health) {
    this.name = name;
    this[monsterHealth] = health;
  }
  // code
}

class Godzilla extends Monster {
  constructor() {
    super('Godzilla', 10000);
  }
}
// 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
var set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.size;    // 3
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
var items = new Set([1,2,3,4,5]);

for (let num of items) {
  console.log(num);
}
// map
var json = { name: "Aaron" };

var map = new Map();

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

// map.get method
map.get('name');   // Aaron
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);
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
// 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
// Promise instance
promise.then(function(result){
  console.log(result);  // Stuff worked!
}, function(err) {
  console.log(err);     // Error: it broke
});
// 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;
  });
// 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;
});
// 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
// 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;
}
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 }
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
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}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment