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
- Compilers/Polyfills/Transpilers
- Babel REPL
- Change Presents to match existing repo (i.e. es2015, react, stage-1) https://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=es2015%2Creact%2Cstage-1&code=
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 Scopinglet
(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 outputReferenceError: 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
tovar
, although withlet
babel will name avoid collisions (try in Babel REPL, it renames duplicate declarations in different blocks i.e. _id2, etc) properly unlikevar
”, i.e. Babel outputsDuplicate declaration "id"
when name collision usinglet
doesn’t pinpoint the causeconsole.log(id); let id = 12;
- Example 1: Babel let limitations. i.e. The following code with variable declaration after its being used
outputs
* * * 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
andreturn
keywords - Handle the
this
keyword within functions (easier to understand)
- Avoids keystrokes like
- Benefits
// 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 ofthis
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 1
* * 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
- Not need
- Example 1 - Shorthand
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}`;