Javascript 2015



Javascript 2015

0 0


javascript-2015-presentation

Why use ES6 now, how to use ES6 now, 24 features of ES6 to use... now!

On Github turnbullm / javascript-2015-presentation

Javascript 2015

Spot the Javascript

#1

var title = ['Welcome', 'To', 'Javascript', '2015']
  .join(' ')
  .toLowerCase()

#2

define "title" = create Array("Welcome", "To", "Javascript", "2015")
  then, implode with ' '
  then, stringToLower
var title = ['Welcome', 'To', 'Javascript', '2015']
  .join(' ')
  .toLowerCase()

Who decided this is Javascript?

  • Variables created
  • Array created
  • Name of functions

???

  • Some of you may be thinking
  • A skin disease?

Let's check their website...

Not much help...

  • Organisation
  • Not for profit (explains the website...)
  • Create standards
    • CD-ROM file structure
    • C#
    • JSON
    • And something they call...
  • MANY standards
  • Been around since Dad was born (1961)

ECMAScript

  • Generic scripting language
  • Any language can implement ECMAScript
  • Including;
    • Actionscript
    • And...Javascript!

ECMAScript Version 5

aka ES5

Finalized in 2009

Object.keys()
Date.toISOString()
Date.now()
JSON.parse()
JSON.stringify()
String.trim()
Array.isArray()
Array.indexOf()
Array.forEach()
Array.map()
Array.filter()
  • (last)
  • Notice how many of these features we use today

That was 2009

Let's fast forward...

Javascript has exploded

In 2015

ECMAScript Version 6

aka ES6

aka ES2015

What's new?

What you're about to learn

  • 24 new features of ES6
    • Code examples
    • Best practises
  • How do start coding ES6 today
    • Using a transpiler
    • Bundling modules
  • Why change Javascript?
  • Why should I care?
  • Too cutting edge
  • Browser support

7 reasons

To start using ES6

right now

1. It's a standard

  • It's not an experimental new framework
  • It's not going to disappear
  • It's here to stay
  • This is how you write Javascript now, starting now!

2. Amazing new stuff

  • Classes
  • Default parameters
  • Constants
  • Modules
  • Promises
  • Template literals
  • Sets
  • Maps
  • ...MORE!
  • These features are amazing
  • As you will soon see
  • They have existed in some Javascript transpilers;
  • Coffeescript, Typescript

3. One standard way

  • Who's used a javascript library for promises?
    • Q
    • Bluebird
    • jQuery deferred
    • ES6 Promises
    • One standard way!
  • Less to memorise & learn
  • Easier to jump between projects
  • Implemented in browsers
    • Optimized
    • Less bloated 3rd party libraries

3. One standard way

  • Who's used a javascript library for promises?
    • Q
    • Bluebird
    • jQuery deferred
    • ES6 Promises
    • One standard way!
  • Less to memorise & learn
  • Easier to jump between projects
  • Implemented in browsers
    • Optimized
    • Less bloated 3rd party libraries
  • Talk about Coffeescript

4. Don't get left behind

  • ES7 / ES2016 will be released next year!
  • Javascript is evolving quickly

Today

  • We use ES5 features without thinking
  • We start to use new ES6 features

Soon

  • We use ES6 features without thinking
  • We start to use new ES7 features

Start using it now, or be left behind!

5. Frameworks

  • Angular 2.0
  • React Native
  • Ember.js

These are all popular and influential frameworks

They are all using ES6 now!

  • These frameworks are leveraging the power of ES6
  • Ember.js - right now has implemented ES6 modules and promises
  • You can write ES5 with these frameworks.
    • It will be clunky in comparison
    • Documentation and online code will use ES6

6. Cleaner code

  • Easier to read
  • Easier to use
  • Easier to maintain
  • You'll see soon :)

7. It's AWESOME!

  • Javascript is maturing
  • Javascript is becoming more powerful
  • Javascript is catching up to other languages
  • These are exciting times!

WAIT

What about browser support?

ES6 Browser Support

November 2015

IE10 7% Safari 6 12% IE11 16% Safari 7 & 8 21% Safari 9 54% Chrome 65% Firefox 74% IE Edge 84%

Support will improve over time

Support will improve over time

It doesn't matter

It doesn't matter

  • Transpiler
  • Converts ES6 to ES5
fancyES6Code();
regularES5Code();

 

fancySassCode
regularCssCode

Usage

  • Command line
    > babel main.js
  • NPM package
    // main.js
    
    require("babel-core/register");
    
    // ES6 here
  • Browser
    <script src="babel-core/browser.js"></script>
    
    <script src="main.es6.js"></script>
    
  • Gulp
    .pipe(babel())
  • Grunt
    grunt.initConfig({
      "babel": {}
    })

Used by

  • Facebook
  • Netflix
  • Mozilla
  • Yahoo
  • Paypal
  • Airbnb
  • Reddit
  • Spotify
  • Atlassian
  • Soundcloud
  • Vimeo
  • Slack
  • Evernote
  • LinkedIn

Babel is amazing

Let's learn ES6

24 new features

Barry    I love you
Anthony     Stop staring at me

Ready?

var myBestFriend = 'Barry';

if (true) {
  var myBestFriend = 'Anthony';
}

console.log( myBestFriend ); // ???
What is logged to the console?
var myBestFriend = 'Barry';

if (true) {
  var myBestFriend = 'Anthony';
}

console.log( myBestFriend ); // Anthony
What is logged to the console?
var numbers = [1, 5, 10];

// Add numbers together

var result = 0;

for (var i = 0; i < numbers.length; i++) {
  var number = numbers[i];
  result += number;
}

console.log( result ); // 16
console.log( i ); // 3
console.log( number ); // 10
  • Var has a large scope
  • Often an unintended side-effect
  • Can produce strange bugs

#1. Let keyword

Limits variable to block scope

let numbers = [1, 5, 10];
let result = 0;
for (let i = 0; i < numbers.length; i++) {
  let number = number[i];
  result += number;
}
console.log( result ); // 16
console.log( i ); // undefined
console.log( number ); // undefined

This is how most would expect var to behave

Use let instead of var

(unless you really need var scoping)

#2. Constant

Value that cannot be altered

const myConstant = 'never changes';

myConstant = 'can I change it?'; // NO!

Use const instead of let

Guarantees references won't be overridden

(if you need references overriden, then use let)

New string functions

  • #3. string.startsWith()
  • #4. string.endsWith()
  • #5. string.includes()
const testString = 'testing';

testString.startsWith('t'); // true
testString.startsWith('test'); // true
testString.startsWith('e', 1); // true
testString.startsWith('est', 1); // true

testString.endsWith('ing'); // true
testString.endsWith('tin', 1); // true

testString.includes('sti'); // true

#6. Array.from()

Converts objects to an array

const myArray = Array.from(notAnArray);

Works with either;

  • Objects that are iterable (can be looped)
  • Objects with a length property, and indexed elements

Array.from(document.querySelectorAll('img'));
Array.from(jQuery('img'));
Array.from("test"); // ["t", "e", "s", "t"]

New array functions

  • #7. array.find()
  • #8. array.findIndex()
const friends = [
  {
    name: 'Barry',
    species: 'Bear'
  },
  {
    name: 'Anthony',
    species: 'Anteater'
  }
];

const myBestFriend = friends.find(function(friend) {
  return friend.name === 'Barry';
});
console.log( myBestFriend ); // { name: 'Barry', species: 'Bear' }

const myBestFriendIndex = friends.findIndex(function(friend) {
  return friend.name === 'Barry';
});
console.log( myBestFriendIndex ); // 0
const sentence = joinWords('I', 'love', 'Barry');
Create the joinWords function
function joinWords(word1, word2, word3) {
  // Join the words and return them
}
function joinWords() {
  const words = Array.prototype.slice.call(arguments);
  // Join the words and return them
}
There must be a better way...
const words = ['I', 'love', 'Barry'];
Call joinWords with an array
// Could try alter joinWords function to accept an array?
const sentence = joinWords(words);
const sentence = joinWords.apply(null, words);
Use spread!

#9. Spread operator

Allows expressions to be expanded into arguments

Represented by 3 dots

const words = ['I', 'love', 'Barry'];
const sentence = joinWords(...words);
const sentence = joinWords('I', 'love', 'Barry'); // Same thing!
function joinWords(...words) {
  // 'words' is an array!
}
const sentence = joinWords(...['I', 'love', 'Barry']);
const words = ['I', 'love'];
const sentence = joinWords(...words, 'my', ...['friend', 'Barry']);
const words = ['I', 'love', 'Barry'];
const wordsCopy = [...words];
const position = [50, 200];
I want 'x' and 'y' constants
const x = position[0];
const y = position[1];
Use destructuring!
const [x, y] = position;

#10. Destructuring

Works with spreads

const [a, b, ...rest] = [1, 2, 3, 4, 5];
console.log( rest ); // [3, 4, 5]

Works even better with objects

function getPosition() {
  return { x: 50, y: 200, z: 13 };
}
const { x, y } = getPosition();
const { z : zIndex } = getPosition();
console.log( zIndex ); // 13

#11. Object shorthand

function getPosition(something) {
  const x, y, z;

  // Do some calculation magic
  // Populate x, y, z constants

  // ES5
  return {
    x: x,
    y: y,
    z: z
  };

  // ES6
  return {
    x,
    y,
    z
  };
}

#12. Template literals

const name = 'Bob';
const activity = 'running';
const place = 'library';
// ES5
const sentence = name + ' is ' + activity + ' to the ' + place;
// ES6
const sentence = `${name} is ${activity} to the ${place}`;
// ES5

const story = '"My name is ' + name + '" said ' + name + '.\n'
  + '"I\'m ' + activity + ' to the ' + place + '", he continued.\n'
  + 'Then ' + name + ' went ' + activity + ' to the ' + place + '.';
// ES6

const story = `"My name is ${name}" said ${name}.
"I'm ${activity} to the ${place}", he continued.
Then, ${name} went ${activity} to the ${place}.`;
  • Backticks
  • New lines
  • Quotes and double quotes
  • Escape character in I\'m
function displayMessage(message) {
  // Some magic here to display the message
}

function removeMessage(message) {
  // Some magic here to remove the message
}

Stop duplicate messages from displaying

Stop duplicate messages from displaying

function displayMessage(message) {
  // Some magic here to display the message
}

function removeMessage(message) {
  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = [];

function displayMessage(message) {
  // Some magic here to display the message
}

function removeMessage(message) {
  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = [];

function displayMessage(message) {

  messagesDisplayed.push(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = [];

function displayMessage(message) {

  messagesDisplayed.push(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  messagesDisplayed.splice(messagesDisplayed.indexOf(message), 1);

  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = [];

function displayMessage(message) {

  if (messagesDisplayed.indexOf(message) > -1) {
    return false;
  }

  messagesDisplayed.push(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  messagesDisplayed.splice(messagesDisplayed.indexOf(message), 1);

  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = [];

function displayMessage(message) {

  if (messagesDisplayed.indexOf(message) > -1) {
    return false;
  }

  messagesDisplayed.push(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  messagesDisplayed.splice(messagesDisplayed.indexOf(message), 1);

  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = [];

function displayMessage(message) {

  // Check if message has already been displayed
  if (messagesDisplayed.indexOf(message) > -1) {
    return false;
  }

  messagesDisplayed.push(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  // Remove message as displayed
  messagesDisplayed.splice(messagesDisplayed.indexOf(message), 1);

  // Some magic here to remove the message
}

#13. Sets

A new type of object

const mySet = new Set();

Store unique values of any type

mySet.add('test');
mySet.add({ testObject: true });

Similar to an array, but easier to use:

mySet.has('test'); // true
mySet.delete('test');
mySet.clear();
mySet.size;
mySet.keys();
mySet.values();
mySet.entries();
mySet.forEach();

Stop duplicate messages from displaying

function displayMessage(message) {
  // Some magic here to display the message
}

function removeMessage(message) {
  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = new Set();

function displayMessage(message) {
  // Some magic here to display the message
}

function removeMessage(message) {
  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = new Set();

function displayMessage(message) {

  messagesDisplayed.add(message);

  // Some magic here to display the message
}

function removeMessage(message) {
  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = new Set();

function displayMessage(message) {

  messagesDisplayed.add(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  messagesDisplayed.delete(message);

  // Some magic here to remove the message
}

Stop duplicate messages from displaying

const messagesDisplayed = new Set();

function displayMessage(message) {

  if (messagesDisplayed.has(message)) {
    return false;
  }

  messagesDisplayed.add(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  messagesDisplayed.delete(message)

  // Some magic here to remove the message
}
const messagesDisplayed = new Set();

function displayMessage(message) {

  if (messagesDisplayed.has(message)) {
    return false;
  }

  messagesDisplayed.add(message);

  // Some magic here to display the message
}

function removeMessage(message) {

  messagesDisplayed.delete(message)

  // Some magic here to remove the message
}
Pow!

What if hundreds of messages are displayed?

const testMessage = {
  contents: 'Barry is such a handsome bear'
};

displayMessage(testMessage);

const importantMessage = {
  contents: 'Anteaters suck',
  important: true
};

displayMessage(importantMessage);

// etc... (hundreds of these)

What if hundreds of messages are displayed?

const messagesDisplayed = new Set();

function displayMessage(message) {

  if (messagesDisplayed.has(message)) {
    return false;
  }

  messagesDisplayed.add(message);

  // Some magic here to display the message
}
  • The Set will become full of message objects
  • The message objects can never be garbage collected
  • Which means... memory leak!

#14. Weak Sets

Similar to the Set object

Used for storing objects

  • If an object is removed...
  • And references are garbage collected...
  • The object is automatically removed from the Weak Set
  • No more memory leak!

Use Weak Sets when storing objects

const weakSet = new WeakSet();
weakSet.add(object);
weakSet.has(object);
weakSet.delete(object);
  • These are the only functions you get!
  • Much simpler than the Set object
    • No iterating over the objects
    • No counting the number of objects

#15. Maps

Another type of object
const myMap = new Map();
Very similar to a Set, but with key-value pairs
myMap.set('testKey', 'testValue');

May have used objects in ES5

const myMap = {
  'testKey': 'testValue'
}

Maps are more readable, and easier to use

const myMap = new Map();
myMap.set('testKey', 'testValue');
myMap.has('testKey'); // true
myMap.get('testKey'); // 'testValue'
myMap.delete('testKey');
myMap.clear();
myMap.size;
myMap.keys();
myMap.values();
myMap.entries();
myMap.forEach();

#16. Weak Maps

The same concept as Weak Set

const weakMap = new WeakMap();
weakMap.set(object, value);
weakMap.has(object);
weakMap.get(object);
weakMap.delete(object);

The keys are weak, not the value

Iterating arrays in ES5

Lots of choices

None are great

for (var index = 0; index < myArray.length; index++) {
  var value = myArray[index];
  // ...
}
  • A lot of code
myArray.forEach(function(value, index) {
  // ...
});
  • Can't use 'break' or 'continue' keywords
  • New scope
for (var index in myArray) {
  var value = myArray[index];
  // ...
}
  • Built for looping over object keys only
  • Can loop over array in random order

Iterating arrays in ES6

One choice

It's great!

#16. for of

for (let value of myArray) {
  // ...
}
for (let index of myArray.keys()) {
  // ...
}
for (let [index, value] of myArray.entries()) {
  // ...
}

Works for any iterable object

  • Arrays
  • Strings
  • Maps
  • Sets

For everything else, either;

  • Make it an iterable object (not enough time to show you today)
  • Convert to an array using Array.from()
  • (last)
  • Can iterate over ANYTHING!

#17. Default parameters

function displayMessage(content, priority = 1) {
  console.log(`Priority: $(priority)`);

  // Some magic here to display the message
}

displayMessage('Hello, I am a message'); // Priority: 1
displayMessage('Something went wrong!', 10); // Priority: 10
Always put default parameters last
Avoid using too many parameters
function displayMessage(content, priority = 1, position = { x: 0, y: 0 }, title = null) {
  // Some magic here to display the message
}
displayMessage('Default message');
displayMessage('Centered message', 1, { x: '50%', y: '50%'});
displayMessage('Error message with title', 10, { x: 0, y: 0 }, 'Uh oh');
  • Confusing to read
  • Embedding default parameters
    • What if they changed?
Use destructuring!
function displayMessage({ content, priority = 1, position = { x: 0, y: 0 }, title = null }) {
  // Some magic here to display the message
}
displayMessage({
  content: 'Default message'
});

displayMessage({
  content: 'Centered message',
  position: { x: '50%', y: '50%' }
});

displayMessage({
  content: 'Error message with title',
  priority: 10,
  title: 'Uh oh'
});
  • Easier to read
  • Override defaults only if needed
I love you
const defaultMessage = {
  content: 'Default message'
};

const centeredMessage = {
  content: 'Centered message',
  position: { x: '50%', y: '50%' }
};

const errorMessage = {
  content: 'Error message with title',
  priority: 10,
  title: 'Uh oh'
};

Is there a better way to represent a message?

#18. Class

Custom type of object

Has its own functions and properties

class Message {
  getContent() {
    return 'Hello, I am a message';
  }
}

const myMessage = new Message();

console.log( myMessage.getContent() ); // Hello, I am a message
Class names should be UpperCamelCase

All these lines of code to create messages?!

class TestMessage {
  getContent() {
    return 'Hello, I am a message';
  }
}
const myTestMessage = new TestMessage();

class ErrorMessage {
  getContent() {
    return 'Something went wrong!';
  }
}
const myErrorMessage = new ErrorMessage();
A class represents a template for creating objects

Just one class / template - Message

const myMessage = new Message();
myMessage.setContent('Hello, I am a message');
console.log( myMessage.getContent() ); // Hello, I am a message!

const errorMessage = new Message();
errorMessage.setContent('Something went wrong!');
console.log( errorMessage.getContent() ); // Something went wrong!
class Message {
  setContent(content) {
    this.content = content;
  }
  getContent() {
    return this.content;
  }
}
Aw yeah!

#19. Class Getters & Setters

  • Use like a regular object
  • Less function calls
  • Cleaner syntax
const myMessage = new Message();
myMessage.setContent('Hello, I am a test message');
myMessage.content = 'Hello, I am a test message';
console.log( myMessage.getContent() );
console.log( myMessage.content );
class Message {
  set content(content) {
    this.content = content;
  }
  get content() {
    return this.content;
  }
}
I see a bug!
class Message {
  set content(content) {
    this.content = content;
  }
}

This may make it easier to spot

class Message {
  setContent(content) {
    this.setContent(content);
  }
}

Maximum call stack size exceeded

class Message {
  set content(content) {
    this.content = content;
    this._content = content;
  }

  get content() {
    return this.content;
    return this._content;
  }
}
Use a leading underscore _
for private properties and functions

This seems like a lot of work... why bother?

You're right!

A regular object does getters and setters

const myMessage = {};
myMessage.content = 'Hello, I am a test message';
console.log( myMessage.content ); // Hello, I am a test message
class Message {
  // Empty
}
const myMessage = new Message();
myMessage.content = 'Hello, I am a test message';
console.log( myMessage.content ); // Hello, I am a test message
myMessage.whatever = 'Anything goes';
console.log( myMessage.whatever ); // Anything goes

Let's get clever with classes

class Message {

  set priority(priority) {

    if (priority < 1 || priority > 10) {
      throw new Error('Priority must be between 1 and 10');
    }

    this._priority = priority;

  }

  get priority() {
    return this._priority;
  }
}
const myMessage = new Message();
myMessage.priority = 20; // ERROR: Priority must be between 1 and 10
myMessage.priority = 5;
console.log( myMessage.priority ); // 5
class Message {
  get log() {
    return `${this.timestamp} - [${this.priority}] ${this.content}`;
  }
}
const myMessage = new Message();
myMessage.content = 'Hello, this is a test message';
myMessage.priority = 5;
myMessage.timestamp = new Date();
console.log( myMessage.log );
// Mon Nov 16 2015 20:24:01 GMT+0000 (GMT) - [5] Hello, this is a test message

Wouldn't it be nice if timestamp was automatically set?

#20. Class constructors

Function that runs when the class is created

class Message {
  constructor() {
    this.timestamp = new Date();
    this.priority = 5;
  }
}
const myMessage = new Message();
console.log( myMessage.timestamp );
// Mon Nov 16 2015 20:24:01 GMT+0000 (GMT)

Construtors accept parameters too

class Message {
  constructor(content) {
    this.content = content;
    this.timestamp = new Date();
    this.priority = 5;
  }
}
const testMessage = new Message('This is a test message');

#21. Sub classing

Allows a class to inherit everything from another class

class DebugMessage extends Message {
  // Empty
}

class ErrorMessage extends Message {
  // Empty
}
const myDebugMessage = new DebugMessage('I am a debug message');
const myErrorMessage = new ErrorMessage('I am an error message');

Everything still works!

class Message {
  setContent(content) {
    this.content = content;
  }
}
class ErrorMessage extends Message {
  setContent(content) {
    content = content + '!!!';
    super.setContent(content);
  }
}
const myMessage = new Message('I am a message');
console.log( myMessage.content ); // I am a message

const myErrorMessage = new ErrorMessage('I am an error message');
console.log( myErrorMessage.content ); // I am an error message!!!
class Message {
  set content(content) {
    this._content = content;
  }
}
class ErrorMessage extends Message {
  set content(content) {
    content = content + '!!!';
    super.content = content
  }
}
class Message {
  constructor(content) {
    this.content = content;
    this.priority = 5;
  }
}
class DebugMessage extends Message {
  constructor(content) {
    super(content);
    this.priority = 1;
  }
}
const myMessage = new Message('I am a message');
console.log( myMessage.priority ); // 5

const myDebugMessage = new DebugMessage('Just testing');
console.log( myErrorMessage.priority ); // 1

#22. Arrow functions

New syntax to easily create anonymous functions

// ES5
const debugMessages = messages.filter(function(message) {
  return message.priority === 1;
});
//ES6
const debugMessages = messages.filter((message) => {
  return message.priority === 1;
});
const debugMessages = messages.filter((message) => {
  return message.priority === 1;
});
  • If the anonymous function is just 1 single line
    • The { and } can be removed
    • The statement is automatically returned
const debugMessages = messages.filter((message) => message.priority === 1);
  • If the anonymous function has just 1 parameter
    • The ( and ) can be removed
const debugMessages = messages.filter(message => message.priority === 1);
// ES5 anonymous function

function(param1, param2) {
  // ...
}
// ES6 arrow function

(param1, param2) => {
  // ...
}

They look different... so what?

What's wrong with this code?

class Message {

  constructor() {
    this.priority = 5;
  }

  increasePriorityEverySecond() {
    setInterval(function() {
      this.priority = this.priority + 1;
    }, 1000);
  }

}
class Message {
  increasePriorityEverySecond() {
    var self = this;
    setInterval(function() {
      self.priority = self.priority + 1;
    }, 1000);
  }
}
class Message {
  increasePriorityEverySecond() {
    setInterval(function() {
      this.priority = this.priority + 1;
    }.bind(this), 1000);
  }
}

Arrow functions retain this from parent scope

class Message {
  increasePriorityEverySecond() {
    setInterval(() => {
      this.priority = this.priority + 1;
    }, 1000);
  }
}
class Message {
  increasePriorityEverySecond() {
    setInterval(() => this.priority = this.priority + 1, 1000);
  }
}
class Message {
  increasePriority() {
    this.priority = this.priority + 1
  }
  increasePriorityEverySecond() {
    setInterval(() => this.increasePriority(), 1000);
  }
}
Yessss
// Globals
var myModule = window.myModule;

// AMD / RequireJS
define(['myModule'], function(myModule) { });

// CommonJS / npm
module.exports = myModule;
var myModule = require('myModule');

// ES6
export myModule;
import { myModule } from 'myModule';
  • No decisions to make
  • It's baked right into Javascript
// Globals
var myModule = window.myModule;

// AMD / RequireJS
define(['myModule'], function(myModule) {});

// CommonJS / npm
module.exports = myModule;
var myModule = require('myModule');

// ES6 modules
export myModule;
import { myModule } from 'myModule';

#23. Modules

// message.js

export class Message {
  // ...
}
// main.js

import { Message } from './message';

const myMessage = new Message();

Multiple exports

// message.js

export class Message {
  // ...
}
export function displayMessage(message) {
  // ...
}

Multiple imports

// main.js

import { Message, displayMessage } from './message';

const myMessage = new Message();

displayMessage(myMessage);
// /vendor/message.js

export class Message {}
// /app/message.js

export class Message {}

Named imports

import { Message as MessageVendor } from '/vendor/message.js';
import { Message as MessageApp } from '/app/message.js';

The ES6 Module Problem

ES6 does not specify a standard for loading files

ES6 only specifies a standard forthe import & export syntax

import { nope } from '../the/browser/does/not/know/how/to/load/this/file';

Solution

Use a bundling tool that understands ES6 modules
  • jspm
Setup your bundling tool to understand ES6 modules
  • Browserify - 'Babelify'
  • Webpack - Babel loader

Prepare yourself...

testInternetConnection(function() {
  fetchMessagesFromServer(function(messages) {
    messages = JSON.parse(messages);
    if (messages[0] == null) {
      displayError('Could not email first message');
      logger.log('First message does not exist');
    } else {
      sendEmail(
        'test@example.com',
        'Here is the first message: ' + messages[0],
        function() {
          displayMessage('Success!');
        },
        function(error) {
          displayError('Could not email first message');
          logger.log('Error sending email: ' + error);
        }
      );
    }
  }, function(error) {
    displayError('Could not email first message');
    logger.log('Error fetching messages from server: ' + error);
  });
}, function(error) {
  displayError('Could not email first message');
  logger.log('Internet connection test failed: ' + error);
});

Is this better?

testInternetConnection({
  onComplete: function() {
    fetchMessagesFromServer({
      onComplete: function(messages) {
        messages = JSON.parse(messages);
        if (messages[0] == null) {
          displayError('Could not email first message');
          logger.log('First message does not exist');
        } else {
          sendEmail({
            email: 'test@example.com',
            message: 'Here is the first message: ' + messages[0],
            onComplete: function() {
              displayMessage('Success!');
            },
            onError: function(error) {
              displayError('Could not email first message');
              logger.log('Error sending email: ' + error);
            }
          });
        }
      },
      onError: function(error) {
        displayError('Could not email first message');
        logger.log('Error fetching messages from server: ' + error);
      }
    });
  },
  onError: function(error) {
    displayError('Could not email first message');
    logger.log('Internet connection test failed: ' + error);
  }
});

There's a better way...

#24. Promises

function fetchMessagesFromServer() {

  // This function takes time
  // Return to caller a Promise object straight away
  // After some time has passed, update the Promise with either;
  // 1. A value (yay)
  // 2. An error (aw)

  return new Promise(function(resolve, reject) {

    jQuery.get('http://example.com/messages.json') // (time passes)
      .done(function(result) {
        // Update the Promise with a value
        resolve(result);
      })
      .fail(function() {
        // Update the Promise with an error
        reject('Could not fetch messages from server');
      })

  });

}
const messages = fetchMessagesFromServer();
console.log( messages ); // Promise - what do I do with this?!
fetchMessagesFromServer()
  .then(function(value) {
    // Promise resolved
    console.log(value);
  })
  .catch(function(value) {
    // Promise rejected
    console.log(value);
  });

So what?

fetchMessagesFromServer()
  .then(function(messages) {
    // ...
  })
  .catch(function(error) {
    // ...
  });
fetchMessagesFromServer(
  function(messages) {
    // ...
  },
  function(error) {
    // ...
  }
);
fetchMessagesFromServer({
  onComplete: function(messages) {
    // ...
  },
  onError: function(error) {
    // ...
  }
});
fetchMessagesFromServer()
  .done(function(messages) {
    // ...
  })
  .fail(function(error) {
    // ...
  });

The power of Promises:

Chaining them together

The function within then() can do 3 types of things;

1. Return another promise

fetchMessagesFromServer()
  .then(function(messages) {
    const promise = sendEmail('test@example.com', messages);
    return promise;
  })
  .then(function(email) {
    // ...
  })
  .catch(function(error) {
    // ...
  });
  
  • If the Promise is resolved...
    • Move to the next 'then' block
  • If the Promise is rejected...
    • Move to the next 'catch' block

The function within then() can do 3 types of things;

2. Return a value

fetchMessagesFromServer()
  .then(function(messages) {
    return messages[0];
  })
  .then(function(firstMessage) {
    // ...
  });
  

This is the same as resolving a Promise

The function within then() can do 3 types of things;

3. Throw an error

fetchMessagesFromServer()
  .then(function(messages) {
    // Try to do something a little crazy
    throw new Error('Uh oh');
  })
  .catch(function(error) {
    // ...
  });
  

This is the same as rejecting a Promise

testInternetConnection()
  .then(function() {
    return fetchMessagesFromServer();
  })
  .then(function(messages) {
    return JSON.parse(messages);
  })
  .then(function(messages) {
    if (messages[0] == null) {
      throw new Error('First message does not exist');
    } else {
      return messages[0];
    }
  })
  .then(function(firstMessage)) {
    return sendEmail(
      'test@example.com',
      'Here is the first message: ' + firstMessage
    );
  })
  .then(function() {
    displayMessage('Success!');
  })
  .catch(function(error) {
    displayError('Could not email first messsage');
    logger.log(error);
  });
  • If an error is thrown within JSON.parse
  • eg: Syntax error due to invalid JSON
  • Then we end up in `catch()`

We can do better!

class MessageService {
  fetchMessagesAsJson() {
    return testInternetConnection()
      .then(fetchMessagesFromServer)
      .then(JSON.parse);
  }
}

class MessageEmailer {
  _getFirstMessage(messages) {
    if (messages[0] == null) {
      throw new Error('First message does not exist');
    } else {
      return messages[0];
    }
  }
  _emailMessage(message) {
    return sendEmail(
      this.email,
      'Here is the message: ' + message
    );
  }
  send(email) {
    MessageService.fetchMessagesAsJson()
      .then(this._getFirstMessage)
      .then(this._emailMessage)
      .then(() => displayMessage('Success!'))
      .catch((error) => {
        displayError('Could not email message');
        console.log(error);
      })
  }
}
  • If an error is thrown within JSON.parse
  • eg: Syntax error due to invalid JSON
  • Then we end up in `catch()`

You made it!

24 features of ES6

Yes, there are more...

  • Symbols
  • Proxies
  • Generators
  • ... EVEN MORE!

Try ES6 now

Links

This turnbullm.github.io/javascript-2015-presentation Summary github.com/lukehoban/es6features Detail es6-features.org Interactive lessons learnharmony.org TDD style lessons es6katas.org Reference developer.mozilla.org

Now you know

  • 7 reasons to use ES6 now
  • How to use ES6 now with Babel
  • 24 features of ES6 to use now!

Final message...

Use ES6 now!

Do it for Barry

Javascript 2015