ES6

Table of Contents

Chapter 1 - ES6

ES6

  • Compatibility Table Kangax ES6
    • Click ECMAScript 6
    • Click the arrow to expand a features
      • Hover over the (C) icon an an example code snippet appears which is the test that runs to see if the current browser has the ES6 feature or not
    • Note: Modules have been separated out of the ES6 Specification and not many browsers implement Modules yet
    • Left Column - ES6 Features
    • Right Columns
      • Compilers/Polyfills/Transpilers
        • Shows percentage of ES6 features implemented
      • Desktop browsers
        • Note IE11 has only 11% implemented, so need to use a Compiler/Polyfill/Transpiler
  • Caniuse.com

  • Babel REPL

    Note: HTL .babelrc file is:

{
    "presets": [
        "es2015",
        "react",
        "stage-1"
    ],
    "plugins": [
        "transform-decorators-legacy",
        "transform-class-properties",
        "add-module-exports"
    ]
}
* Note: "use strict"; automatically appears on the right when es2015 is selected
  • let, const, and Block Scoping
    • let (overcomes Hoisting) - but NOT with Babel
      • Example 1: Babel let limitations. i.e. The following code with variable declaration after its being used outputs undefined when with ES6 it should output ReferenceError: id is not defined
      • @bradleymeck from Babel Slack said this is because: “babel approximates spec in many ways due to perf problems of being compliant. Babel compiles let to var, although with let babel will name avoid collisions (try in Babel REPL, it renames duplicate declarations in different blocks i.e. _id2, etc) properly unlike var”, i.e. Babel outputs Duplicate declaration "id" when name collision using let doesn’t pinpoint the cause
        console.log(id);
        let id = 12;
        

* * * Example 2: Temperal Dead Zone

function F() { 
    // Temperal Dead Zone that compiles runs across even though not yet declared
    a = 1; 
}
let a = null;
F();
console.log(a);

* * * Example 3: For Loop use let instead of var

let a = [];
/**
 * Change from `var` to `let` used in For Loop so each iteration of the
 * loop gets its own `i` variable, and closures created 
 * within it will close over the iteration's value of i
 * (rather than global value of i)
 */
for (var i=0; i<2; i++) {
  a.push(function() { 
    return i; 
  });
}
if (typeof i != 'undefined') {
  console.log("Outside block value of i: ", i);
}
console.log("Response from calling a[0]: ", a[0]());
console.log("Response from calling a[1]: ", a[1]());

* * const (Immutable Variables) * Note: When declaring with const we must initialise with a value

const id = 1;
if (id > 0) {
    const id = 2;
}
console.log(id); // Output is 1

* * Block Scoping (overcomes Function scope reliance) - Declaring variable within braces scope disappears at end of block

let id = 1;
{
    let id = 2;
}
console.log(id); // Output is 1
  • Arrow Functions (Shorthand) => (fat arrow symbol)
    • Benefits
      • Avoids keystrokes like function and return keywords
      • Handle the this keyword within functions (easier to understand)
// Input in parenthesis results in expression as output
var getA = () => 10;
console.log(typeof getA); // Outputs: function
console.log(getA()); // Outputs: 10

* * Example 1: No block required when just complex expression in Arrow function

var calcA = (val1, val2) => val1 * val2;

/**
 *  Equivalent to:
 *  function calcA(val1, val2) { return val1 * val2; }
 */

console.log(calcA(2, 3)); // Outputs: 6

* * Example 2: Use a Block when Arrow function with multiple expressions, but requires return to be used

var calcA = (val1, val2) => {
    return val1 * val2;
}

console.log(calcA(2, 3)); // Outputs: 6

* * Example 3: Arrow functions for Event Listener and this context

/**
* Issue in ES5 since `this` passed in would be set to
* to element that receives the event, but would lose
* the context of the function itself
*/
document.addEventListener('click', function() {
  console.log(this); // Outputs: #document
});

/**
* ES6 Arrow function use `this` passed in would be set to
* to the context of the code being run in global area,
* which is `Window` (the global object), not the element 
* receiving the event in context of a function
*/
document.addEventListener('click', () =>
  console.log(this) // Outputs: Window
);
    • Example 4: Arrow functions with Object Property with function
  • * * Outputs context of function we are in
    var invoice = {
      num: 123,
      process: function() {
    // Outputs: Object { "num": 123, "process": [Function process] }
    console.log(this);
      }
    };
    invoice.process();
    
  • * * Outputs context of code being run (i.e. global window object)
    var invoice = {
      num: 123,
      // Output: true
      process: () => console.log(this == window)
    };
    invoice.process();
    
var invoice = {
  process1: function() {
    return () => {
      console.log(this.process2()); // Outputs: Object
      console.log(this.process3()); // Outputs: Window
    };
  },
  process2: function() {
    return this;
  },
  process3: () => console.log(this)
};
invoice.process1()();
  • * * Cannot use bind, call, or apply new objects to Arrow functions to change value of this like we can with ordinary functions
var invoice = {
  num: 123,
  process: function() {
    // Outputs: 123
    return () => console.log(this.num)
  }
};
var newInvoice = {
  num: 456
};
invoice.process().bind(newInvoice)();
var invoice = {
  num: 123,
  process: function() {
    return function() {
      // Outputs: 456
      console.log(this.num)
    }
  }
};
var newInvoice = {
  num: 456
};
invoice.process().bind(newInvoice)();

* * Example 5: Arrow functions are constructible in the ES2015 preset since hasOwnProperty “prototype” returns true.

Note: @kovensky from Babel on slack said that 
"you can give the `spec: true` option (enables spec transformations for any plugins in 
the preset that allow them) to make it detect construction and throw"
@bradleymeck said "you can't delete function prototypes since they aren't configurable"
@bradleymeck said "basically don't assume babel is 100% to spec, ever. it is compiling 
to an older version which generally can't be 100%
@kovensky said "some things just can't be 100% without real engine support"
var a = () => 10;
console.log(a.hasOwnProperty("prototype")); // Output: true
  • Default Parameters
// Access to variables and functions in context when declaring default values
var z1 = 1;
var z2 = () => 2;
var a = function(x = 10, y = x * 2 + z1 + z2()) {
  console.log(x + y);
  console.log(arguments.length); // Refers to amount of args passed to function (ignores defaults set)
}
a(50, undefined);

* * Dynamic Functions

let funcNames = ["foo", "bar"]; 
let makeDynamic = (name) => new Function(`return function ${name}()\{ alert('called ${name}!') \}`)();
let processNames = () => { for (const value of funcNames) { makeDynamic(`instance_${value}`)(); } };
processNames();
  • Rest Operator

    • Example 1
      var a = (id, ...arr) => {
        console.log(arr instanceof Array);
        console.log(arguments.length); Output: 3
      }
      a(123, 'b1', 'b2');
      

* * Example 2: Outputs empty array if no Rest parameters provided

var a = (id, ...arr) => {
    console.log(arr); // Output: []
}
a(123);
    • Example 3: Dynamic function with Rest parameters
var a = new Function("...groups", "return groups;");
console.log(a("x", "y")); // Outputs: ['x','y']
  • Spread Operator

    • Example 1
var a = [1, 2, 3];
var max = Math.max(...a);
console.log(max); // Outputs: 3

* * Example 2:

var a = [...[,,]];
console.log(a); // Output [undefined, undefined]
  • Object Literal Extensions

    • Example 1 - Shorthand
      • Not need a: a,, etc
var a = 1;
var myField = "feel good";
var z = { 
    a,
    b: 2,
    c: () => a,
    d() {
        return this.b
    },
    "e e"() {
      return 1000;
    },
    [myField + '-001']: true // Dynamic fields allows expression for property
}
console.log(z.c());
console.log(z.d());
console.log(z["e e"]());
console.log(z); // Outputs: Object { ..., "feel good-001"; true }

* * Example 2 - Dynamic field/property name for method

Note: In Babel REPL treats scripts as “module mode” called meaning that using this at the top level is undefined

Dynamic methods of an object may be created using Object Literal Extensions provided by ECMAScript 2015 (ES6):

const postfixes = ['foo', 'bar'];

let mainObj = {};

let makeDynamic = (postfix) => {
  let newMethodName = 'instance: ' + postfix;
  let tempObj = {
    [postfix]: true,
    get [newMethodName] () {
      console.log(`called getter method of ${newMethodName}`);
    },
    set [newMethodName] (value) {
      console.log(`called setter method with ${value}`);
      this.postfix = value;
    }
  }
  Object.assign(mainObj, tempObj);
  return mainObj[newMethodName]();
}

let processPostfixes = (postfixes) => { 
  for (const postfix of postfixes) {
    makeDynamic(postfix); 
  }
};

processPostfixes(postfixes);

console.log(mainObj);

* * Using setters and getters

const postfixes = ['foo', 'bar'];
const newMethodNames = [];
const mainObj = {
  id: null
};

const makeDynamic = (postfix) => {
  const newMethodName = 'instance: ' + postfix;
  newMethodNames.push(newMethodName);
  let tempObj = {
    [postfix]: true,
    get [newMethodName] () {
      console.log(`called getter method of ${newMethodName}`);
    },
    set [newMethodName] (value) {
      console.log(`called setter method with ${value}`);
    }
  }
  Object.assign(mainObj, tempObj);
  return mainObj[newMethodName];
}

const processPostfixes = (postfixes) => { 
  for (const postfix of postfixes) {
    makeDynamic(postfix); 
  }
};

processPostfixes(postfixes);
mainObj[newMethodNames[0]] = false;
console.log(mainObj[newMethodNames[0]])
console.log(mainObj);
  • For Of Loop

    • Example 1 - Loop through iterables (string, arrays)
let a = [,,]; // i.e. [undefined, undefined]
for (const i of a) {
    console.log(i); // undefined, undefined
}
  • Octals and Binary

    • Example 1
let oct = 0o10;
console.log(oct); // 8

let bin = 0b10;
console.log(bin); // 2
  • Template Literals (i.e. string with interpolated vars and expresions)
    • Note: Escape character \

    • Example 1 - Interpolation before function call

var num = '123';
function showNum(msg) {
    let num = '456';
    console.log(msg); // Output: 123
}
showNum(`Number: ${"NO-" + num}`);
  • Tagged Template Literals
    • Note: Segments become unique strings within string literal
var num1 = '123';
var num2 = '456';
function showNum(segments, ...rest) {
    console.log(segments); // Output: 
    console.log(rest);
}
showNo `Number: ${"NO-" + num1 + "-A-" + num2}`;
Written on January 29, 2017