@rob_rich

JavaScript
Tools and Syntax
for the Modern Web

by Rob Richardson

@rob_rich

https://robrich.org/

About Me

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.

ES6, ES7, ES8+

So which is it?

JavaScript Harmony

EcmaScript 6

EcmaScript 2015

ES6


... they're the same thing

EcmaScript versions

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

EcmaScript

... in the browser


The server-side ES story is interesting too,
but we'll focus on the browser story

EcmaScript 6

Two main groups of new things:

  • New Features
  • New Syntax:
      sugar over existing features

Can I use it today?

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

What is Transpiling?

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/

The Tools

Groups of Tools

  • Polyfills: add missing methods at runtime
  • Transpiler: convert newer to similar older code
  • Bundler: polyfill module loading

Polyfills

Can I polyfill the feature?

HTML5 examples:

Yes: html5shiv adds Array methods and additional tags

No: Can't polyfill canvas tag

Transpilers

Using Babel

  1. Visit babeljs.io/docs/setup/
  2. Choose your host
  3. Include the provided code

Using Babel

  1. Visit babeljs.io/repl/
  2. Type stuff

Bundlers

Using Webpack

npm install --save-dev webpack-cli
npm install --save-dev webpack
webpack main.js -o bundle.js

Then reference bundle.js from your html

Using Webpack

webpack.config.js:

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
};
$> webpack

Using Webpack with Babel

webpack.config.js:

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
};

.babelrc:

...

Using Webpack with Babel

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 Presets

@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/

Babel Presets

To use stage-# features, include plugins for each feature.

See babeljs.io/docs/plugins

stage-# presets are deprecated.

TC39 Process

  • stage-0. Strawman: an idea
  • stage-1. Proposal
  • stage-2. Draft: Initial spec
  • stage-3. Candidate: Complete spec, initial browser implementations
  • stage-4. Finished: will go to next yearly release

Source: tc39.github.io/process-document/

Can I use it today?

Yes.

Use WebPack and Babel
to transpile an ES6/7/8 app into an ES5 file.

The Language

Cool Syntax

Sprinkle in sparingly.

Don't use it just to use it.

Use it when it adds clarity
and simplicity to your code.

Code for legibility

Code like the person maintaining your code is a homicidal maniac that knows where you live.

- Jeremy Clark

Variable scoping

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
}

Variable scoping

let and const

  • scoped to curly braces, not to the function

"fat arrow" functions

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);

"fat arrow" functions

=>

  • binds this when defined
  • always anonymous
  • single line implicit last line return
  • function is still best for heavy lifting

"fat arrow" functions

Build 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

"fat arrow" functions

Returning JS Object

function (a) { return {name: a}; }
(a) => { return {name: a}; }
a => {name: a} // <-- BROKEN
a => ({name: a})

Classes

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);
  }
}

Classes

  • Syntactic sugar over prototype chain
  • It makes it feel like C# / Java
  • ... it still works the same

Polyfill Promise

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
});

Polyfill Promise

Asynchronous resolve

  • proxy for a later result
  • alternate style to callbacks
  • religious war: some find it more confusing

Modules

import and export

  • CommonJS module resolution
  • resolves dependencies synchronously
  • can reference only portions of the dependency

Modules

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

Modules

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

Modules

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

String Templates

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."

String Templates

  • Use variables inline
  • Multi-line strings
  • Be careful to html encode user data

Building objects

  • Syntactic sugar for declaring variables more easily

Building objects

ES5:

var x = 3;
var y = 7;
var c = {
    x: x,
    y: y
};

ES6:

var x = 3;
var y = 7;
var c = { x, y };

Building objects

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
};

Dereferencing objects

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

Dereferencing objects

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

Rest parameters

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) {
    // ...
  });
}

Rest parameters

  • Get the rest of the arguments passed as an array
  • Must be the last argument
  • Much better than "arguments" pseudo-array

Spread operator

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);

Spread operator

  • Turn array into a list of parameters
  • Like apply() but doesn't change "this"

Spread operator

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"]

Rest and Spread operators

A more powerful array literal

Rest:

function (a, b, ...theRest) {
  theRest.forEach(function (g) {
    // ...
  });
}

Spread:

var args = [0, 1, 2];
myFunction(...args);

async / await

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
}

async / await

  • Looks synchronous, acts asynchronous
  • Promises under the hood
  • Automatic interop with promises

async / await

Source: bram.us/2017/05/09/javascript-from-callbacks-to-promises-to-asyncawait-in-7-seconds/

Cool Syntax

Remember: sugar over existing features

Sprinkle in sparingly to add legibility

Don't add new features to show off

Tools

ES6,7,8+ -> ES5 babeljs.io
import and export webpack.github.io
Anything else Polyfill as needed

Modern Frameworks

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

Yes!

You can use modern JavaScript today.