A Quick Interpretation of ES6's New Features

Keywords: Javascript ECMAScript Java Attribute

ECMAScript 2015 is an ECMAScript standard approved in June 2015.

ES2015 is an important update of the language and the first major update of the language since ES5 standardization in 2009. These functions are now being implemented in major JavaScript engines.

For the complete specification of ECMAScript 2015, please refer to the ES2015 standard. The following brief introduction is for reference only.

Arrow function and this

The arrow function is a function abbreviation using the => grammar. They are similar in grammar to the functions in C#, Java 8, and CaoffeeScript. They support expressions and body of statements. Unlike functions, arrow functions have the same scope as the code around this. If the arrow function is in another function, it shares the arguments variable of its parent function.

// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

// Lexical this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
};

// Lexical arguments
function square() {
  let example = () => {
    let numbers = [];
    for (let number of arguments) {
      numbers.push(number * number);
    }

    return numbers;
  };

  return example();
}

square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]

class

ES2015 is a simple analogy to the prototype-based OO model. Having a convenient declarative form makes class schemas easier to use and encourages interoperability. Classes support prototype-based inheritance, super calls, instance and static methods, and constructors.

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

Enhanced object literals

Object literals are extended to support prototyping during construction, to provide abbreviations for foo: foo allocation, to define methods, and to make super calls. Together, they make object literal quantities and class declarations more compact and make object-based design more convenient.

var obj = {
    // Sets the prototype. "__proto__" or '__proto__' would also work.
    __proto__: theProtoObj,
    // Computed property name does not set prototype or trigger early error for
    // duplicate __proto__ properties.
    ['__proto__']: somethingElse,
    // Shorthand for 'handler: handler'
    handler,
    // Methods
    toString() {
     // Super calls
     return "d " + super.toString();
    },
    // Computed (dynamic) property names
    [ "prop_" + (() => 42)() ]: 42
};

The _proto_ attribute requires native support and has been discarded in previous versions of ECMAScript. Most engines now support this attribute, but some do not.

Template string

Template strings provide syntax sugar for constructing strings. This is similar to the string interpolation function in Perl, Python, etc. Optionally, tags can be added to allow customized string construction to avoid injection attacks or to construct higher-level data structures from string content.

// Basic literal string creation
`This is a pretty little template string.`

// Multiline strings
`In ES5 this is
 not legal.`

// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// Unescaped template strings
String.raw`In ES5 "\n" is a line-feed.`

// Construct an HTTP request prefix is used to interpret the replacements and construction
GET`http://foo.org/bar?a=${a}&b=${b}
    Content-Type: application/json
    X-Credentials: ${credentials}
    { "foo": ${foo},
      "bar": ${bar}}`(myOnReadyStateChangeHandler);

deconstruction

Deconstruction allows binding using pattern matching and supports matching arrays and objects. Similar to standard object lookup foo["bar"], the value undefined is generated when it is not found.

// list matching
var [a, ,b] = [1,2,3];
a === 1;
b === 3;

// object matching
var { op: a, lhs: { op: b }, rhs: c }
       = getASTNode()

// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()

// Can be used in parameter position
function g({name: x}) {
  console.log(x);
}
g({name: 5})

// Fail-soft destructuring
var [a] = [];
a === undefined;

// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;

// Destructuring + defaults arguments
function r({x, y, w = 10, h = 10}) {
  return x + y + w + h;
}
r({x:1, y:2}) === 23

Default parameter + object expander + object collector

Default parameter values for the callee. Converts an array to a continuous parameter in a function call. Bind the following parameters to an array. Rest arguments more directly meet the need for common situations.

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


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


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

Block-level scopes let and const

Block-wide binding constructs. let is a new var and const is a single task. Static restrictions prevent use before allocation.

function f() {
  {
    let x;
    {
      // this is ok since it's a block scoped name
      const x = "sneaky";
      // error, was just defined with `const` above
      x = "foo";
    }
    // this is ok since it was declared with `let`
    x = "bar";
    // error, already declared above in this block
    let x = "inner";
  }
}

Iterator + For..Of

Iterator objects support custom iterations, such as CLR IEnumerable or Java Iterable. Use generic for..in to iterate for..of based on custom iterators. No need to implement arrays, LINQ and other inert design patterns are enabled.

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) {
  // truncate the sequence at 1000
  if (n > 1000)
    break;
  console.log(n);
}

Iteration is based on these duck-typed interfaces (shown using only TypeScript type syntax):

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

Generators

The generator uses function * and simplified iterator yield. A function declared as function * returns a Generator instance. Generators are subtypes of iterators, including additional next and throw. These allow the value to flow back to the generator, so the yield expression returns a value (or throws it).

Note: It can also be used to enable'await'asynchronous programming, see also the ES7 await proposal.

var fibonacci = {
  [Symbol.iterator]: function*() {
    var pre = 0, cur = 1;
    for (;;) {
      var temp = pre;
      pre = cur;
      cur += temp;
      yield cur;
    }
  }
}

for (var n of fibonacci) {
  // truncate the sequence at 1000
  if (n > 1000)
    break;
  console.log(n);
}

The generator interface is (shown using only TypeScript type syntax):

interface Generator extends Iterator {
    next(value?: any): IteratorResult;
    throw(exception: any);
}

Unicode support

Non-destructive addition of complete Unicode is supported, including new Unicode literal form in strings, new RegExp mode for u-processing code points, and new API s for processing 21-bit code point-level strings. These new features support building global applications in JavaScript.

// same as ES5.1
"A kind of".length == 2

// new RegExp behaviour, opt-in 'u'
"A kind of".match(/./u)[0].length == 2

// new form
"\u{20BB7}" == "A kind of" == "\uD842\uDFB7"

// new String ops
"A kind of".codePointAt(0) == 0x20BB7

// for-of iterates code points
for(var c of "A kind of") {
  console.log(c);
}

Modular

Language-level support for modules defined by components. Coding patterns from popular JavaScript module loaders (AMD, Common JS). Runtime behavior defined by the default loader defined by the host. Implicit Asynchronous Model - Code will not execute until the requested module is available and processed.

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));

Some other functions include export default and export*:

// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
    return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));

Module Formatting
Babel can convert ES2015 modules into several different formats, including Common.js, AMD, System and UMD. You can even create your own.

Module Loading

Not ES2015
This is retained in the implementation definition in the ECMAScript 2015 specification. The final standard will be in the WHATWG Loader specification, but this is currently under way. The following is from the previous ES2015 draft.

Module loader support:

Dynamic Loading
Regional isolation
Global namespace isolation
Compile hook
Nested virtualization

Default module loaders can be configured, and new loaders can be constructed to call and load code in isolated or constrained contexts.

// Dynamic loading – 'System' is default loader
System.import("lib/math").then(function(m) {
  alert("2π = " + m.sum(m.pi, m.pi));
});

// Create execution sandboxes – new Loaders
var loader = new Loader({
  global: fixup(window) // replace 'console.log'
});
loader.eval("console.log(\"hello world!\");");

// Directly manipulate module cache
System.get("jquery");
System.set("jquery", Module({$: $})); // WARNING: not yet finalized

Need additional polyfill
Because Babel defaults to the common.js module, it does not contain the polyfill of the module loader API.

Using Module Loader
To use it, you need to tell Babel to use the system module formatter. Also be sure to check System.js

Map + Set + WeakMap + WeakSet

Efficient data structure of common algorithms. WeakMaps provides a leak-free object key table.

// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set

agent

Agents can create objects with all the behaviors available to host objects. It can be used for interception, object virtualization, logging/analysis, etc.

// Proxying a normal object
var target = {};
var handler = {
  get: function (receiver, name) {
    return `Hello, ${name}!`;
  }
};

var p = new Proxy(target, handler);
p.world === "Hello, world!";
// Proxying a function object
var target = function () { return "I am the target"; };
var handler = {
  apply: function (receiver, ...args) {
    return "I am the proxy";
  }
};

var p = new Proxy(target, handler);
p() === "I am the proxy";

All run-time meta-operations have available methods:

var handler =
{
  // target.prop
  get: ...,
  // target.prop = value
  set: ...,
  // 'prop' in target
  has: ...,
  // delete target.prop
  deleteProperty: ...,
  // target(...args)
  apply: ...,
  // new target(...args)
  construct: ...,
  // Object.getOwnPropertyDescriptor(target, 'prop')
  getOwnPropertyDescriptor: ...,
  // Object.defineProperty(target, 'prop', descriptor)
  defineProperty: ...,
  // Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
  // target.__proto__, object.isPrototypeOf(target), object instanceof target
  getPrototypeOf: ...,
  // Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
  setPrototypeOf: ...,
  // for (let i in target) {}
  enumerate: ...,
  // Object.keys(target)
  ownKeys: ...,
  // Object.preventExtensions(target)
  preventExtensions: ...,
  // Object.isExtensible(target)
  isExtensible :...
}

Unsupported functionality
Due to ES5 limitations, agents cannot be converted or modified. See support in various JavaScript engines.

Symbols

Symbols enables access control for object state. Symbols allow attributes to be typed into string s (such as ES5) or symbol s. Symbols is a new primitive type. Optional parameters used in name debugging - but not part of Symbols. Symbols are unique (such as gensym), but not private, because they expose Object. getOwn Property Symbols through reflection.

(function() {

  // module scoped symbol
  var key = Symbol("key");

  function MyClass(privateData) {
    this[key] = privateData;
  }

  MyClass.prototype = {
    doStuff: function() {
      ... this[key] ...
    }
  };

  // Limited support from Babel, full support requires native implementation.
  typeof key === "symbol"
})();

var c = new MyClass("hello")
c["key"] === undefined

Limited support through polyfill
Limited support requires Babel polyfill. Due to language constraints, some functions cannot be converted or modified.

Subclasses can be built-in

In ES2015, built-in plug-ins like Array, Date and DOM ElementS can be inherited.

// User code of Array subclass
class MyArray extends Array {
    constructor(...args) { super(...args); }
}

var arr = new MyArray();
arr[1] = 12;
arr.length == 2

Partial support
The built-in subclass separability should be evaluated on a case-by-case basis, since HTMLElement classes can be subclassed, while many such as Date, Array and Error cannot be attributed to ES5 engine limitations.

Math + Number + String + Object Added APIs

Many new libraries have been added, including core math libraries, array conversion assistants, and Object.assign for replication.

Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll("*")) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0,0) })

Limited support from polyfill
Babel polyfill supports most of these API s. However, some functions are omitted for various reasons (for example, String.prototype.normalize requires a lot of extra code to support it).

Binary and octal

Two new digital text formats have been added for binary (b) and octal (o).

0b111110111 === 503 // true
0o767 === 503 // true

Support text only
babel can only transform 0o767 but not Number("0o767").

Promises

Promises is a library for asynchronous programming. Promise is the first type of representation of values that can be provided in the future. Promises are used in many existing JavaScript libraries.

function timeout(duration = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, duration);
    })
}

var p = timeout(1000).then(() => {
    return timeout(2000);
}).then(() => {
    throw new Error("hmm");
}).catch(err => {
    return Promise.all([timeout(100), timeout(200)]);
})

Reflect API

Reflect API exposes runtime metaoperations of objects. This is actually a reversal of the Proxy API and allows calls to the same meta-operation as the proxy. Especially suitable for the realization of proxy.

var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;

Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]

function C(a, b){
  this.c = a + b;
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42

Tail Recursive Call

Make sure that tail placement calls do not increase the stack indefinitely. In the case of unbounded input, the recursive algorithm is safe.

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 ES2015
factorial(100000)

End, original English: https://github.com/lukehoban/....

Posted by stangoe on Sun, 25 Aug 2019 08:07:24 -0700