ECMAScript 6 – Features overview – What's new



ECMAScript 6 – Features overview – What's new

0 0


es6-features

ES6 Features

On Github miguelcoba / es6-features

ECMAScript 6

Features overview

Created by Miguel Cobá / @MiguelCobaMtz

ECMAScript 6

A.K.A. ECMAScript 2015

Standard ECMA-262

Try it

What's new

Features

Arrows

Classes

Modules

Enhanced Object Literal

Template Strings

Destructuring

Default + Rest + Spread

Features

Let + Const

Iterator + For..Of

Map + Set + WeakMap + WeakSet

Proxies

Symbols

Promises

Tail Calls

Arrows

Function shorthand using the => syntax

Arrow

  • Similar to analogous feature in CoffeeScript, Java 8 and C#
  • Support block or expression bodies
  • Share the same lexical this as their surrounding code

Arrow

Expression body

Returns the value of the expression

Arrow

Expression body - Single argument

Classic

var numbers = [1, 2, 3];
numbers.map(function(v) { return v * v }); // [1, 4, 9];
						

ECMAScript 6

var numbers = [1, 2, 3];
numbers.map( v => v * v ); // [1, 4, 9];
						

Arrow

Expression body - Multiple arguments

Classic

var numbers = [1, 2, 3];
numbers.map(function(v, idx) { return v * idx }); // [0, 2, 6];
						

ECMAScript 6

var numbers = [1, 2, 3];
numbers.map( (v, idx) => v * idx ); // [0, 2, 6];
						

Arrow

Expression body

Classic

var numbers = [1, 2, 3];
numbers.map( function(v, idx) { return { i: idx, v: v.toString() }; } );
// [{ i: 0, v: "1" }, { i: 1, v: "2" }, { i: 2, v: "3" }];
						

ECMAScript 6

var numbers = [1, 2, 3];
numbers.map( (v, idx) => ({ i: idx, v: v.toString() }) );
// [{ i: 0, v: "1" }, { i: 1, v: "2" }, { i: 2, v: "3" }];
						

Arrow

Block body

Explicit return value, or undefined if no especified

Arrow

Block body - Explicit return value

Classic

var words = ['some', 'random', 'words'];
words.filter( function(w) {
  var l = w.length;
  return l % 2 === 0; 
}); // ['some', 'random'];
						

ECMAScript 6

var words = ['some', 'random', 'words'];
words.filter( w  => {
  var l = w.length;
  return l % 2 === 0; 
}); // ['some', 'random'];
						

Arrow

Block body - No return value

Classic

var words = ['some', 'random', 'words'];
words.forEach( function(w) {
  console.log('Word:', w);
});
// Word: some
// Word: random
// Word: worlds
						

ECMAScript 6

var words = ['some', 'random', 'words'];
words.forEach( w  => { // no return value
  console.log('Word:', w);
});
// Word: some
// Word: random
// Word: worlds
						

Classes

Syntactic sugar over prototype-based inheritance

Does not introduces a new object-oriented inheritance model

Provide a much simpler and clearer syntax to create objects and dealing with inheritance

Classes

Class body and method definitions

  • The body of a class is the part that is in curly brackets {}
  • The constructor is a special method named: constructor
  • Properties are accessed through this
  • Methods are defined by putting their name, followed by any arguments within parenthesis, and then the block of code of the function
  • Accessors can be defined to allow more control on property read/write operations

Classes

Classic

var Person = function (name) {
  this.name = name;
};
Person.prototype.sayHello = function() {
  console.log("Hello, I'm " + this.name);
};
var john = new Person("John");
john.sayHello(); // Hello, I'm John
console.log('name:', john.name); // name: John
						

Classes

ECMAScript 6

class Person {
  constructor (name) {
    this.name = name; // properties accessed with this keyword
  }
  get nickname() {
    return 'lil ' + this.name; // nickname only readable, never writable
  }
  sayHello() { //
    console.log("Hello, I'm " + this.name);
  }
}
var john = new Person('John');
john.sayHello(); // Hello, I'm John
console.log('name:', john.name); // name: John
console.log('nickname:', john.nickname); // Nickname: lil John
john.nickname = 'Johnny';  // Error
						

Classes - Subclasses

Classic

function Student(name, subject) { // Define the Student constructor
  Person.call(this, name); // Calls parent constructor
  this.subject = subject;
};
// Create a Student.prototype object that inherits from Person.prototype.
Student.prototype = Object.create(Person.prototype);
// Set the "constructor" property to refer to Student
Student.prototype.constructor = Student;
// Override the "sayHello" method
Student.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.name + ". I'm studying " + this.subject + ".");
};
// Add a "sayGoodBye" method
Student.prototype.sayGoodBye = function(){
  console.log("Goodbye!");
};

var janet = new Student("Janet", "Applied Physics");
janet.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
janet.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(janet instanceof Person);  // true 
console.log(janet instanceof Student); // true
						

Classes - Subclasses

ECMAScript 6

class Student extends Person {
  constructor(name, subject) {
    super(name); // Calls parent constructor
    this.subject = subject;
  }
  // Override the "sayHello" method
  sayHello() {
    console.log("Hello, I'm " + this.name + ". I'm studying " + this.subject + ".");
  }
  // Add a "sayGoodBye" method
  sayGoodBye(){
    console.log("Goodbye!");
  };
}

var janet = new Student("Janet", "Applied Physics");
janet.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
janet.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(janet instanceof Person);  // true 
console.log(janet instanceof Student); // true
						

Modules

  • Each module is defined in its own file
  • Functions or variables defined in a module are not visible outside unless you explicitly export them
  • Two kinds of exports: named exports (several per module) and default exports (one per module)
  • To export certain variables from a module you just use the keyword export
  • to consume the exported variables in a different module you use import

Modules

Named exports (several per module)

// circles.js
export const PI = 3.1415;
export function area(radius) {
  return PI * radius * radius;
}
export function perimeter(radius) {
  return 2 * PI * radius;
}

// app.js
import { area, perimeter } from 'circles'; // import some objects
console.log(area(5)); // 78.54
console.log(perimeter(3)); // 18.85
						

Modules

Named exports (several per module) 2

// circles.js
export const PI = 3.1415;
export function area(radius) {
  return PI * radius * radius;
}
export function perimeter(radius) {
  return 2 * PI * radius;
}

// app.js
import * as circles from 'circles'; // import whole module
console.log(circles.area(5)); // 78.54
console.log(circles.perimeter(3)); // 18.85
						

Modules

Default exports (one per module)

// library.js
export default function () { ... };

// app.js
import f from 'library'; // You can import it and use any name to refer to it
f();
						

Modules

Default and named exports

 // underscore.js
export default function (obj) {
  ...
};
export function each(obj, iterator, context) {
  ...
}
export { each as forEach }; // some alias for an export

// app.js
import _, { each } from 'underscore'; 
// default import with no braces, named imports inside braces
						

Enhanced Object Literals

  • Setting prototype at construction
  • Shorthand for foo: foo properties
  • Defining methods
  • Making super calls
  • Property names with expressions

Enhanced Object Literals

ECMAScript 6

function SomeConstructor () {};
SomeConstructor.prototype.someMethod = function() {
  console.log('someMethod executed');
}
var obj = {
  // __proto__
  __proto__: SomeConstructor.prototype,
  // Shorthand for ‘parseInt: parseInt’
  parseInt,
  // Methods
  toString() {
    // Super calls
    return "d: " + super.toString();
  },
  // Computed (dynamic) property names
  [ 'prop_' + (() => 42)() ]: 42
};

console.log(obj.parseInt('2')*obj.parseInt('-1'));  // -2
console.log(obj.toString()); // d: [object Object]
console.log(obj.prop_42); // 42
console.log(obj.someMethod.call()); // 'someMethod executed'
						

Template Strings

Syntactic sugar for constructing strings

String literals allowing embedded expressions

Allow

  • Multi-line strings
  • String interpolation
  • Embedded expressions
  • String formatting
  • String tagging

Template Strings

Enclosed by the back-tick `

They can contain placeholders indicated by dollar sign and braces: ${ }

Template strings

String substitution

Classic

var name = "Miguel";
console.log('Hi, ' + name + '!'); // Hi, Miguel!
						

ECMAScript 6

var name = "Miguel";
console.log(`Hi, ${name}!`); // Hi, Miguel!
						

Template strings

Expression interpolation

Classic

var a = 5;
var b = 10;
console.log("Fifteen is " + (a + b) + " and\nnot " + (2 * a + b) + ".");
// "Fifteen is 15 and
// not 20."
						

ECMAScript 6

var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`);
// "Fifteen is 15 and
// not 20."
						

Template strings

Expression interpolation 2

function fn() { return "I am a result. Rarr"; }
console.log(`foo ${fn()} bar`);
//=> foo I am a result. Rarr bar.

var user = {name: 'Caitlin Potter'};
console.log(`Thanks for getting this into V8, ${user.name.toUpperCase()}.`);
// Thanks for getting this into V8, CAITLIN POTTER.
						

Template strings

Multi-line strings

Classic

console.log("string text line 1\n"+
"string text line 2");
// "string text line 1
// string text line 2"
						

ECMAScript 6

console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"
						

Template strings

Tagged template strings

Tagged Templates transform a Template String by placing a function name before the template string

fn`Hello ${you}! You are looking ${adjective} today!`

// desugars into
fn(["Hello ", "! You are looking ", " today!"], you, adjective);
						

Can be useful for:

  • Escaping of interpolated variables
  • Localisation
  • Formatting
  • Other complex substitutions

Destructuring assignment

Allows binding using pattern matching

Support for arrays and objects

Fail-soft semantics, producing undefined values when impossible to bind a value to a variable

Destructuring assignment

Classic

var foo = ["one", "two", "three"];

var one   = foo[0];
var two   = foo[1];
var three = foo[2];
						

ECMAScript 6

var foo = ["one", "two", "three"];

var [one, two, three] = foo;
						

Destructuring assignment 2

// Assignment without declaration
var a, b;
[a, b] = [1, 2];

// Swapping variables
var a = 1, b = 3;
[a, b] = [b, a];

// Multiple return value
function f() { return [1, 2]; }
var a, b, array;
[a, b] = f(); // 1 to 1 assignment: a is 1, b is 2
array = f(); // store return value as an array: a is [1, 2]

// fail-soft
var [a] = [];
console.log(a === undefined); // true
						

Default + Rest + Spread

Default values: Callee-evaluated default parameter values

Rest arguments: Turn an array into consecutive arguments in a function call

Spread values: passes each element of an array as an single argument

Default + Rest + Spread

Default values: Callee-evaluated default parameter values

function f(x, y=12) {
  // y is 12 if not passed (or passed as undefined)
  return x + y;
}
console.log(f(3) === 15);  // true
						

Default + Rest + Spread

Rest arguments: Turn an array into consecutive arguments in a function call

function f(x, ...y) {
  // y is an Array
  return x * y.length;
}
console.log(f(3, "hello", true) === 6); // true
						

Default + Rest + Spread

Spread values: passes each element of an array as an single argument

function f(x, y, z) {
  return x + y + z;
}
// Pass each elem of array as argument
console.log(f(...[1,2,3]) === 6); // true
						

Let + Const

Block scoped bindings

Let + Const

Let: declares a block scope local variable, optionally initializing it to a value

var a = 5;
var b = 10;

if (a === 5) {
  let a = 4; // The scope is inside the if-block
  var b = 1; // The scope is inside the function

  console.log(a);  // 4
  console.log(b);  // 1
} 

console.log(a); // 5
console.log(b); // 1
						

Let + Const

Const: creates a read-only named constant

const MY_FAV = 7;

MY_FAV = 20; // trying to modify constant after declaration throws an error

const MY_FAV = 20; // trying to redeclare a constant throws an error 

var MY_FAV = 20; // throws error: the name MY_FAV is reserved for constant above

const FOO; // SyntaxError:  const requires an initializer

const MY_OBJECT = {"key": "value"}; // const also works on objects

MY_OBJECT = {"OTHER_KEY": "value"}; // overwriting the object fails as above

MY_OBJECT.key = "otherValue"; // object attributes are not protected
						

Iterator + for..of

Iterator: Establishes "iterable" protocol to allow objects to customize their iteration behaviour

for..of: convenient operator to iterate over all values of an iterable object

Iterator + for..of

let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return { done: false, value: cur }
      }
    }
  }
}
for (var n of fibonacci) {
  if (n > 1000) // truncate the sequence at 1000
    break;
  console.log(n);
}
						

Iterator + for..of

Iteration works if the code complies with those interfaces

// TypeScript syntax
interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;
}
interface Iterable {
  [Symbol.iterator](): Iterator
}
						

Map + Set + WeakMap + WeakSet

Map

  • Simple key/value map
  • Any value (both objects and primitive values) may be used as either a key or a value

Map

var myMap = new Map();

var keyObj = {},
    keyFunc = function () {},
    keyString = "a string";

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

myMap.size; // 3

// getting the values
myMap.get(keyString);    // "value associated with 'a string'"
myMap.get(keyObj);       // "value associated with keyObj"
myMap.get(keyFunc);      // "value associated with keyFunc"

myMap.get("a string");   // "value associated with 'a string'"
                         // because keyString === 'a string'
myMap.get({});           // undefined, because keyObj !== {}
myMap.get(function() {}) // undefined, because keyFunc !== function () {}
						

Set

  • Lets you store unique values
  • Can store primitive values or object references

Set

var mySet = new Set();

mySet.add(1);
mySet.add(5);
mySet.add("some text");
var o = {a: 1, b: 2};
mySet.add(o);

mySet.has(1); // true
mySet.has(3); // false, 3 has not been added to the set
mySet.has(5);              // true
mySet.has(Math.sqrt(25));  // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true

mySet.size; // 4

mySet.delete(5); // removes 5 from the set
mySet.has(5);    // false, 5 has been removed

mySet.size; // 3, we just removed one value
						

WeakMap

  • Collection of key/value pairs in which the keys are weakly referenced
  • The keys must be objects and the values can be arbitrary values
  • Primitive data types as keys are not allowed
  • The key in a WeakMap is held weakly: if there are no other strong references to the key, then the entire entry will be removed from the WeakMap by the garbage collector.

WeakMap

var wm = new WeakMap();
// As there is no other reference to the key, the whole entry 
// will be garbage collected
wm.set({}, { extra: 42 });
console.log(wm.size === undefined); // true
console.log(wm.has({})); // false
						

WeakSet

  • The WeakSet object lets you store weakly held objects in a collection
  • References to objects in the collection are held weakly: If there is no other reference to an object stored in the WeakSet, they can be garbage collected
  • There is no list of current objects stored in the collection
  • WeakSets are not enumerable.

WeakSet

// No other reference to the object added, so it is garbage collected
var ws = new WeakSet();
ws.add({ data: 42 });
console.log(ws.has({ data: 42 })); // false

// obj holds a reference to the object so is not garbage collected
var obj = {a: 'string'};
ws.add(obj);
console.log(ws.has(obj)); // true
						

Proxies

Proxies

  • Used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc)
  • Can be used for interception, object virtualization, logging/profiling, etc.

Proxy - Object

var target = { world: 'Mars'};
var handler = {
  get: function (receiver, name) {
    return `Hello, ${name}! from ${receiver[name]}`;
  }
};

var p = new Proxy(target, handler);
console.log(p.world === 'Hello, world! from Mars'); // true
						

Proxy - Function

var target = function () { return 'I am the target'; };
var handler = {
  apply: function (receiver, ...args) {
    return 'I am the proxy, and ' + receiver(args);
  }
};

var p = new Proxy(target, handler);
console.log(p() === 'I am the proxy, and I am the target'); // true
						

Symbols

Symbols

  • New primitive type
  • They are tokens that serve as unique IDs
  • have an optional string-valued parameter that lets you give the symbol a description
  • Can be used as an identifier for object properties
  • Using symbols instead of strings allows different modules to create properties that don't conflict with one another

Symbols

var sym1 = Symbol();
var sym2 = Symbol("foo");
var sym3 = Symbol("some description");

// It creates a new symbol each time
console.log(Symbol("foo") === Symbol("foo")); // false

// Can be used as a property key
var sym = Symbol("foo");
var obj = {
  [sym]: 1
};
console.log(obj[sym]); // 1
						

Promise

Promise

  • Used for deferred and asynchronous computations
  • represents an operation that hasn't completed yet, but is expected in the future

Promise

var someLongProcess = function() {
  console.log(`starting: ${new Date()}`);
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('some value');
    }, 2000);
  });
};

someLongProcess().then(function(value) {
  console.log(`ending with ${value}: ${new Date()}`);
});
						

Tail calls

Tail calls

  • A tail call is a subroutine call performed as the final action of a procedure
  • Calls in tail-position are guaranteed to not grow the stack unboundedly
  • Recursive algorithms are guaranteed constant-space

Tail calls

function factorial(n, acc = 1) {
  'use strict';
   if (n <= 1) return acc;
   return factorial(n - 1, n * acc);
}

// Stack overflow in most implementations today,
// but safe on arbitrary inputs in ES6
factorial(100000)
						

References

Thank you!

Miguel Cobá

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
ECMAScript 6 Features overview Created by Miguel Cobá / @MiguelCobaMtz