by Rob Richardson
Rob Richardson is a software craftsman building web properties in ASP.NET and Node, Angular and Vue. He's a frequent speaker at conferences, user groups, and community events, and a diligent teacher and student of high quality software development. You can find this and other talks on https://robrich.org/presentations and follow him on twitter at @rob_rich.
JavaScript Harmony
EcmaScript 6
EcmaScript 2015
ES6
... they're the same thing
Version | Date | Features |
1 | 6/1997 | |
2 | 6/1998 | |
3 | 12/1999 | |
4 | Abandoned | |
5 | 12/2009 | strict mode, JSON, standards |
5.1 | 6/2011 | standards |
6 | 6/2015 | classes, modules, iterators, generators, arrow functions, typed arrays, collections, promises, let, const, Array methods, etc |
7 | 6/2016 | **, Array#includes |
8 | 6/2017 | async / await |
9 | 6/2018 | rest, spread, async loops |
source: wikipedia.org/wiki/ECMAScript
... in the browser
The server-side ES story is interesting too,
but we'll focus on the browser story
Two main groups of new things:
Yes but ...
Browser support:
kangax.github.io/compat-table/es6/
www.caniuse.com
spoiler: Evergreen browsers will always lag, but you can "transpile" to older standards and use new things today
Transpiling is a specific term for taking source code written in one language and transforming into another language that has a similar level of abstraction.
Source: https://www.stevefenton.co.uk/2012/11/compiling-vs-transpiling/
Can I polyfill the feature?
HTML5 examples:
Yes: html5shiv adds Array methods and additional tags
No: Can't polyfill canvas tag
npm install --save-dev webpack-cli
npm install --save-dev webpack
webpack main.js -o bundle.js
Then reference bundle.js from your html
webpack.config.js:
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
$> webpack
webpack.config.js:
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
};
.babelrc:
...
webpack.config.js:
...
.babelrc:
{
"presets": ["@babel/preset-env"]
}
Then run these from the command line
npm install --save-dev babel-loader @babel/core @babel/preset-env
webpack
@babel/present-env
includes all the released features,
and transpiles to suport all active browsers.
Optionally configure target browsers using browserlist queries.
See also https://browserl.ist/
To use stage-# features, include plugins for each feature.
stage-# presets are deprecated.
Source: tc39.github.io/process-document/
Yes.
Use WebPack and Babel
to transpile an ES6/7/8 app into an ES5 file.
Sprinkle in sparingly.
Don't use it just to use it.
Use it when it adds clarity
and simplicity to your code.
Code like the person maintaining your code is a homicidal maniac that knows where you live.
ES5: var
function hello() {
console.log(message);
// undefined
if (true) {
var message = 'hello world';
console.log(message);
// 'hello world'
}
console.log(message);
// 'hello world'
}
ES6: let, const
function hello() {
console.log(message);
// exception
if (true) {
let message = 'hello world';
console.log(message);
// 'hello world'
}
console.log(message);
// exception
}
ES5: function
var array = [1,2,3];
array.map(function (a) {
return a * 2;
});
ES6: =>
var array = [1,2,3];
array.map(a => a * 2);
this
when definedBuild it up
function (a) { return a * 2; }
(a) => { return a * 2; }
a => { return a * 2; }
a => a * 2
(a) => a * 2
All of these are valid ES6 syntax
Returning JS Object
function (a) { return {name: a}; }
(a) => { return {name: a}; }
a => {name: a} // <-- BROKEN
a => ({name: a})
ES5: constructor function
function Person(name) {
this.name = name;
}
Person.prototype.speak = function () {
console.log('hello '+this.name);
};
ES6: class
class Person {
constructor(name) {
this.name = name;
}
speak() {
console.log('hello '+this.name);
}
}
Asynchronous resolve
ES5: callback
function doWork(cb) {
cb(null, 'results');
}
doWork(function (err, results) {
if (err) {
// handle error
}
// handle success
});
ES6: promise
function doWork() {
return new Promise(function (success, fail) {
success('result');
});
}
doWork().then(function (result) {
// handle success
}).catch(err) {
// handle error
});
Asynchronous resolve
import
and export
ES5:
ES6: export and import
// -------- lib.js --------
export function square(x) {
return x * x;
}
// -------- main.js --------
import * as lib from './lib';
lib.square(4); // 16
Node:
// -------- lib.js --------
function square(x) {
return x * x;
};
module.exports = {
square: square
};
// -------- main.js --------
var lib = require('./lib');
lib.square(4); // 16
ES6: export and import
// -------- lib.js --------
export function square(x) {
return x * x;
}
// -------- main.js --------
import * as lib from './lib';
lib.square(4); // 16
Node:
// -------- lib.js --------
function square(x) {
return x * x;
}
module.exports = {
default: square
};
// -------- main.js --------
var lib = require('./lib').default;
lib(4); // 16
ES6: export and import
// -------- lib.js --------
export default function (x) {
return x * x;
}
// -------- main.js --------
import lib from './lib';
lib(4); // 16
ES5: add strings
var a = 5;
var b = 10;
var msg = "Fifteen is " + (a + b) + " and\n";
message += "not " + (2 * a + b) + ".");
console.log(msg);
// "Fifteen is 15 and
// not 20."
ES6: string templates
var a = 5;
var b = 10;
var msg = `Fifteen is ${a + b} and
not ${2 * a + b}.`;
console.log(msg);
// "Fifteen is 15 and
// not 20."
ES5:
var x = 3;
var y = 7;
var c = {
x: x,
y: y
};
ES6:
var x = 3;
var y = 7;
var c = { x, y };
ES5:
var x = 3;
var y = 7;
var c = {
x: x,
y: y,
z: 9
};
ES6:
var x = 3;
var y = 7;
var c = {
x,
y,
z: 9
};
ES5:
var c = { x: 3, y: 7 };
var x = c.x;
var y = c.y;
console.log(x); // 3
ES6:
var c = { x: 3, y: 7 };
let { x, y } = c;
console.log(x); // 3
ES5:
ES6:
// -------- lib.js --------
export function square(x) {
return x * x;
}
export function double(x) {
return x * 2;
}
// -------- main.js --------
import { square, double } from './lib';
square(4); // 16
double(4); // 8
ES5:
function (a, b) {
var theRest = Array.prototype.slice.call(arguments);
theRest.shift(); // a
theRest.shift(); // b
theRest.forEach(function (g) {
// ...
});
}
ES6:
function(a, b, ...theRest) {
theRest.forEach(function (g) {
// ...
});
}
Pass an array like a series of arguments
ES5:
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction.apply(null, args);
ES6:
function myFunction(x, y, z) { }
var args = [0, 1, 2];
myFunction(...args);
A more powerful array literal
ES5:
var parts = ['shoulders', 'knees'];
// duplicate array
var lyrics = parts.slice();
lyrics.unshift('head');
lyrics.push('and');
lyrics.push('toes');
// ["head", "shoulders", "knees",
// "and", "toes"]
ES6:
var parts = ['shoulders', 'knees'];
var lyrics = [
'head',
...parts,
'and',
'toes'
];
// ["head", "shoulders", "knees",
// "and", "toes"]
A more powerful array literal
Rest:
function (a, b, ...theRest) {
theRest.forEach(function (g) {
// ...
});
}
Spread:
var args = [0, 1, 2];
myFunction(...args);
ES6: promises
function doWork() {
return new Promise(function (success, fail) {
success('result');
});
}
doWork().then(function (result) {
// handle success
}).catch(err) {
// handle error
});
ES8: async / await
async function doWork() {
// something asynchronous
return 'result';
}
try {
let result = await doWork();
} catch (err) {
// handle error
}
Source: bram.us/2017/05/09/javascript-from-callbacks-to-promises-to-asyncawait-in-7-seconds/
Remember: sugar over existing features
Sprinkle in sparingly to add legibility
Don't add new features to show off
ES6,7,8+ -> ES5 | babeljs.io |
import and export | webpack.github.io |
Anything else | Polyfill as needed |
React (create-react-app) |
Webpack & Babel with JSX plugin |
Angular CLI | Webpack & TypeScript |
Vue.js CLI | Webpack & Babel or TypeScript |
Aurelia CLI | Webpack & Babel or TypeScript |
You can use modern JavaScript today.