<commit_before>##
##  es6-features -- ECMAScript 6 Feature Overview & Comparison
##  Copyright (c) 2015 <NAME> <<EMAIL>>
##
##  Permission is hereby granted, free of charge, to any person obtaining
##  a copy of this software and associated documentation files (the
##  "Software"), to deal in the Software without restriction, including
##  without limitation the rights to use, copy, modify, merge, publish,
##  distribute, sublicense, and/or sell copies of the Software, and to
##  permit persons to whom the Software is furnished to do so, subject to
##  the following conditions:
##
##  The above copyright notice and this permission notice shall be included
##  in all copies or substantial portions of the Software.
##
##  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
##  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
##  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
##  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
##  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
##  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
##  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##


Constants
=========

Constants
---------

Support for constants (immutable variables).

6| |const PI| = 3.141593;
6| PI > 3.0;

5| //  only in ES5 through the help of object properties
5| //  and only in global context and not in a block scope
5| Object.|defineProperty|(typeof global === "object" ? global : window, "|PI|", {
5|     value:        3.141593,
5|     enumerable:   true,
5|     |writable:     false|,
5|     |configurable: false|
5| })
5| PI > 3.0;


Scoping
=======

Block-Scoped Variables
----------------------

Block-scoped variables (and constants) without hoisting.

6| for (|let i| = 0; i < a.length; i++) {
6|     |let x| = a[i];
6|     ...
6| }
6| for (|let i| = 0; i < b.length; i++) {
6|     |let y| = b[i];
6|     ...
6| }
6|
6| let callbacks = [];
6| for (|let| i = 0; i <= 2; i++) {
6|     callbacks[i] = function () { return i * 2; };
6| }
6| callbacks[0]() === 0;
6| callbacks[1]() === 2;
6| callbacks[2]() === 4;

5| |var i, x, y|;
5| for (i = 0; i < a.length; i++) {
5|     x = a[i];
5|     ...
5| }
5| for (i = 0; i < b.length; i++)
5|     y = b[i];
5|     ...
5| }
5|
5| var callbacks = [];
5| for (|var| i = 0; i <= 2; i++) {
5|     |(function (i) {|
5|         callbacks[i] = function() { return i * 2; };
5|     |})(i);|
5| }
5| callbacks[0]() === 0;
5| callbacks[1]() === 2;
5| callbacks[2]() === 4;


Block-Scoped Functions
----------------------

Block-scoped function definitions.

6| {
6|     function foo () { return 1; }
6|     foo() === 1;
6|     {
6|         function |foo| () { return |2|; }
6|         |foo|() === |2|;
6|     }
6|     foo() === 1;
6| }

5| //  only in ES5 with the help of block-scope emulating
5| //  function scopes and function expressions
5| (function () {
5|     var foo = function () { return 1; }
5|     foo() === 1;
5|     |(function () {|
5|         var |foo| = function () { return |2|; }
5|         |foo|() === |2|;
5|     |})();|
5|     foo() === 1;
5| })();

Arrow Functions
===============

Expression Bodies
-----------------

More expressive closure syntax.

6| odds  = evens.map(|v =>| v + 1);
6| pairs = evens.map(|v => (|{ even: v, odd: v + 1 }|)|);
6| nums  = evens.map(|(v, i) =>| v + i);

5| odds  = evens.map(|function (v) { return| v + 1; |}|);
5| pairs = evens.map(|function (v) { return| { even: v, odd: v + 1 }; |}|);
5| nums  = evens.map(|function (v, i) { return| v + i; |}|);

Statement Bodies
----------------

More expressive closure syntax.

6| nums.forEach(|v => {|
6|    if (v % 5 === 0)
6|        fives.push(v);
6| |}|)

5| nums.forEach(|function (v) {|
5|    if (v % 5 === 0)
5|        fives.push(v);
5| |}|);

Lexical `this`
--------------

More intuitive handling of current object context.

6| |this|.nums.forEach(|(v) =>| {
6|     if (v % 5 === 0)
6|         |this|.fives.push(v);
6| });

5| //  variant 1
5| var |self| = |this|;
5| |this|.nums.forEach(|function (v)| {
5|     if (v % 5 === 0)
5|         |self|.fives.push(v);
5| });
5|
5| //  variant 2 (since ECMAScript 5.1 only)
5| |this|.nums.forEach(|function (v)| {
5|     if (v % 5 === 0)
5|         |this|.fives.push(v);
5| }|.bind(this)|);


Extended Parameter Handling
===========================

Default Parameter Values
------------------------

Simple and intuitive default values for function parameters.

6| function f (x, y |= 7|, z |= 42|) {
6|     return x + y + z;
6| }
6| f(1) === 50;

5| function f (x, y, z) {
5|     |if (y === undefined)|
5|         |y = 7|;
5|     |if (z === undefined)|
5|         |z = 42|;
5|     return x + y + z;
5| };
5| f(1) === 50;

Rest Parameter
--------------

Aggregation of remaining arguments into single parameter of variadic functions.

6| function f (x, y, |...a|) {
6|     return (x + y) * a.length;
6| }
6| f(1, 2, |"hello", true, 7|) === 9;

5| function f (x, y) {
5|     |var a = Array.prototype.slice.call(arguments, 2)|;
5|     return (x + y) * a.length;
5| };
5| f(1, 2, |"hello", true, 7|) === 9;

Spread Operator
---------------

Spreading of elements of an interable collection (like an array or even
a string) into both literal elements and individual function parameters.

6| var params = [ "hello", true, 7 ];
6| var other = [ 1, 2, |...params| ]; // [ 1, 2, "hello", true, 7 ]
6| f(1, 2, |...params|) === 9;
6|
6| var str = "foo";
6| var chars = [ ... str ]; // [ "f", "o", "o" ]

5| var params = [ "hello", true, 7 ];
5| var other = [ 1, 2 ].|concat(params)|; // [ 1, 2, "hello", true, 7 ]
5| f|.apply|(|undefined, [| 1, 2 |].concat(params)|) === 9;
5|
5| var str = "foo";
5| var chars = str.split(""); // [ "f", "o", "o" ]

Template Strings
================

String Interpolation
--------------------

Intuitive expression interpolation for single-line and multi-line strings.

6| var customer = { name: "Foo" };
6| var card = { amount: 7, product: "Bar", unitprice: 42 };
6| message = `Hello |${customer.name}|,
6| want to buy |${card.amount} ${card.product}| for
6| a total of |${card.amount * card.unitprice}| bucks?`;

5| var customer = { name: "Foo" };
5| var card = { amount: 7, product: "Bar", unitprice: 42 };
5| message = "Hello "| + customer.name + |",\n" +
5| "want to buy "| + card.amount + |" "| + card.product + |" for\n" +
5| "a total of "| + (card.amount * card.unitprice) + |" bucks?";

Custom Interpolation
--------------------

Flexible expression interpolation for arbitrary methods.

6| get`http://example.com/foo?bar=|${|bar + baz|}|&quux=|${|quux|}|`;

5| get|([ |"http://example.com/foo?bar="|,| "&quux="|,| ""| ],|bar + baz|,| quux|)|;

Raw String Access
-----------------

Access the raw template string content (backslashes are not interpreted).

6| function quux (strings, ...values) {
6|     strings[0] === "|foo\n|";
6|     strings[1] === "bar";
6|     strings|.raw|[0] === "|foo\\n|";
6|     strings.raw[1] === "bar";
6|     values[0] === 42;
6| }
6| quux `foo|\n|${ 42 }bar`
6|
6| String.raw `foo|\n|${ 42 }bar` === "foo|\\n|42bar";

5| //  no equivalent in ES5


Extended Literals
=================

Binary & Octal Literal
----------------------

Direct support for safe binary and octal literals.

6| |0b|111110111 === 503;
6| |0o|767 === 503;

5| |parseInt(|"111110111"|, 2)| === 503;
5| |parseInt(|"767"|, 8)| === 503;
5| |0|767 === 503; // only in non-strict, backward compatibility mode

Unicode String & RegExp Literal
-------------------------------

Extended support using Unicode within strings and regular expressions.

6| "𠮷".length === 2;
6| "𠮷".match(/./|u|)[0].length === 2;
6| "|\u{20BB7}|" === "𠮷" === "\uD842\uDFB7";
6| "𠮷".|codePointAt|(0) == 0x20BB7;
6| for (let codepoint |of| "𠮷") console.log(codepoint);

5| "𠮷".length === 2;
5| "𠮷".match(/(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/)[0].length === 2;
5| "𠮷" === "\uD842\uDFB7";
5| //  no equivalent in ES5
5| //  no equivalent in ES5


Enhanced Regular Expression
===========================

Regular Expression Sticky Matching
----------------------------------

Keep the matching position sticky between matches and
this way support efficient parsing of arbitrary long input strings,
even with an arbitrary number of distinct regular expressions.

6| let parser = (input, match) => {
6|     for (let pos = 0, lastPos = input.length; |pos < lastPos|; ) {
6|         for (let i = 0; i < match.length; i++) {
6|             |match[i].pattern.lastIndex = pos|;
6|             let found;
6|             if ((found = match[i].pattern.exec(input)) !== null) {
6|                 match[i].action(found);
6|                 |pos = match[i].pattern.lastIndex|;
6|                 break;
6|             }
6|         }
6|     }
6| }
6|
6| let report = (match) => {
6|     console.log(JSON.stringify(match));
6| };
6| parser("Foo 1 Bar 7 Baz 42", [
6|     { pattern: /^Foo\s+(\d+)/|y|, action: (match) => report(match) },
6|     { pattern: /^Bar\s+(\d+)/|y|, action: (match) => report(match) },
6|     { pattern: /^Baz\s+(\d+)/|y|, action: (match) => report(match) },
6|     { pattern: /^\s*/|y|,         action: (match) => {}            }
6| ]);

5| var parser = function (input, match) {
5|     for (var i, found, |inputTmp = input|; |inputTmp !== ""|; ) {
5|         for (i = 0; i < match.length; i++) {
5|             if ((found = match[i].pattern.exec(inputTmp)) !== null) {
5|                 match[i].action(found);
5|                 |inputTmp = inputTmp.substr(found[0].length)|;
5|                 break;
5|             }
5|         }
5|     }
5| }
5|
5| var report = function (match) {
5|     console.log(JSON.stringify(match));
5| };
5| parser("Foo 1 Bar 7 Baz 42", [
5|     { pattern: /^Foo\s+(\d+)/, action: function (match) { report(match); } },
5|     { pattern: /^Bar\s+(\d+)/, action: function (match) { report(match); } },
5|     { pattern: /^Baz\s+(\d+)/, action: function (match) { report(match); } },
5|     { pattern: /^\s*/,         action: function (match) {}                 }
5| ]);


Enhanced Object Properties
==========================

Property Shorthand
------------------

Shorter syntax for common object property definition idiom.

6| obj = { |x|, |y| };

5| obj = { |x: x|, |y: y| };

Computed Property Names
-----------------------

Support for computed names in object property definitions.

6| obj = {
6|     foo: "bar",
6|     |[| "prop_" + foo() |]:| 42
6| };

5| obj = {
5|     foo: "bar"
5| };
5| |obj[| "prop_" + foo() |] =| 42;

Method Properties
-----------------

Support for method notation in object property definitions,
for both regular functions and generator functions.

6| obj = {
6|     |foo (a, b)| {
6|         ...
6|     },
6|     |bar (x, y)| {
6|         ...
6|     },
6|     |*quux (x, y)| {
6|         ...
6|     }
6| };

5| obj = {
5|     foo|: function| (a, b) {
5|         ...
5|     },
5|     bar|: function| (x, y) {
5|         ...
5|     },
5|     //  quux: no equivalent in ES5
5| };


Destructuring Assignment
========================

Array Matching
--------------

Intuitive and flexible destructuring of Arrays into individual variables during assignment.

6| var list = [ 1, 2, 3 ];
6| var |[ a, , b ] = list|;
6| [ |b, a| ] = [ |a, b| ];

5| var list = [ 1, 2, 3 ];
5| var |a = list[0]|, |b = list[2]|;
5| var |tmp = a|; |a = b|; |b = tmp|;

Object Matching, Shorthand Notation
-----------------------------------

Intuitive and flexible destructuring of Objects into individual variables during assignment.

6| var |{ op, lhs, rhs }| = getASTNode();

5| var |tmp| = getASTNode();
5| var |op  = tmp.op|;
5| var |lhs = tmp.lhs|;
5| var |rhs = tmp.rhs|;

Object Matching, Deep Matching
------------------------------

Intuitive and flexible destructuring of Objects into individual variables during assignment.

6| var { op: a, |lhs: { op: b }|, rhs: c } = getASTNode();

5| var tmp = getASTNode();
5| var a = tmp.op;
5| var |b = tmp.lhs.op|;
5| var c = tmp.rhs;

Parameter Context Matching
--------------------------

Intuitive and flexible destructuring of Arrays and Objects into individual parameters during function calls.

6| function f (|[ name, val ]|) {
6|     console.log(name, val);
6| }
6| function g (|{ name: n, val: v }|) {
6|     console.log(n, v);
6| }
6| function h (|{ name, val }|) {
6|     console.log(name, val);
6| }
6| f([ "bar", 42 ]);
6| g({ name: "foo", val:  7 });
6| h({ name: "bar", val: 42 });

5| function f (|arg|) {
5|     |var name = arg[0]|;
5|     |var val  = arg[1]|;
5|     console.log(name, val);
5| };
5| function g (|arg|) {
5|     |var n = arg.name|;
5|     |var v = arg.val|;
5|     console.log(n, v);
5| };
5| function h (|arg|) {
5|     |var name = arg.name|;
5|     |var val  = arg.val|;
5|     console.log(name, val);
5| };
5| f([ "bar", 42 ]);
5| g({ name: "foo", val:  7 });
5| h({ name: "bar", val: 42 });

Fail-Soft Destructuring
-----------------------

Fail-soft destructuring, optionally with defaults.

6| var list = [ 7, 42 ];
6| var [ a = 1, b = 2, |c = 3|, |d| ] = list;
6| a === 7;
6| b === 42;
6| c === |3|;
6| d === |undefined|;

5| var list = [ 7, 42 ];
5| var a = typeof list[0] !== "undefined" ? list[0] : 1;
5| var b = typeof list[1] !== "undefined" ? list[1] : 2;
5| var c = typeof list[2] !== "undefined" ? list[2] : |3|;
5| var d = typeof list[3] !== "undefined" ? list[3] : |undefined|;
5| a === 7;
5| b === 42;
5| c === |3|;
5| d === |undefined|;


Modules
=======

Symbol Export/Import
--------------------

Support for exporting/importing symbols from/to modules without global namespace pollution.

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

5| //  lib/math.js
5| |LibMath = {}|;
5| |LibMath.sum| = function (x, y) { return x + y };
5| |LibMath.pi| = 3.141593;
5|
5| //  someApp.js
5| var |math = LibMath|;
5| console.log("2π = " + math.sum(math.pi, math.pi));
5|
5| //  otherApp.js
5| var |sum = LibMath.sum|, |pi = LibMath.pi|;
5| console.log("2π = " + sum(pi, pi));

Default & Wildcard
------------------

Marking a symbol as the default exported symbol and mass-mixin of symbols.

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

5| //  lib/mathplusplus.js
5| |LibMathPP = {}|;
5| |for (symbol in LibMath)|
5|     |if (LibMath.hasOwnProperty(symbol))|
5|         |LibMathPP[symbol] = LibMath[symbol]|;
5| |LibMathPP.e| = 2.71828182846;
5| |LibMathPP.exp| = function (x) { return Math.exp(x) };
5|
5| //  someApp.js
5| var |exp = LibMathPP.exp|, |pi = LibMathPP.pi|, |e = libMathPP.e|;
5| console.log("e^{π} = " + exp(pi));


Classes
=======

Class Definition
----------------

More intuitive, OOP-style and boilerplate-free classes.

6| |class| Shape {
6|     |constructor| (id, x, y) {
6|         this.id = id;
6|         this.move(x, y);
6|     }
6|     |move| (x, y) {
6|         this.x = x;
6|         this.y = y;
6|     }
6| }

5| var Shape = |function| (id, x, y) {
5|     this.id = id;
5|     this.move(x, y);
5| };
5| Shape.|prototype.move| = |function| (x, y) {
5|     this.x = x;
5|     this.y = y;
5| };

Class Inheritance
-----------------

More intuitive, OOP-style and boilerplate-free inheritance.

6| |class| Rectangle |extends| Shape {
6|     constructor (id, x, y, width, height) {
6|         |super|(id, x, y);
6|         this.width  = width;
6|         this.height = height;
6|     }
6| }
6| |class| Circle |extends| Shape {
6|     constructor (id, x, y, radius) {
6|         |super|(id, x, y);
6|         this.radius = radius;
6|     }
6| }

5| var Rectangle = function (id, x, y, width, height) {
5|     |Shape.call|(this, id, x, y);
5|     this.width  = width;
5|     this.height = height;
5| };
5| |Rectangle.prototype = Object.create(Shape.prototype)|;
5| |Rectangle.prototype.constructor = Rectangle|;
5| var Circle = function (id, x, y, radius) {
5|     |Shape.call|(this, id, x, y);
5|     this.radius = radius;
5| };
5| |Circle.prototype = Object.create(Shape.prototype)|;
5| |Circle.prototype.constructor = Circle|;

Class Inheritance, From Expressions
-----------------------------------

Support for mixin-style inheritance by extending from expressions yielding
function objects. [Notice: the generic `aggregation` function is usually provided
by a library like [this one](https://github.com/rse/aggregation), of course]

6| var |aggregation| = (baseClass, ...mixins) => {
6|     let base = class _Combined extends baseClass {
6|         constructor (...args) {
6|             |super|(...args);
6|             mixins.forEach((mixin) => {
6|                 mixin.prototype.|initializer|.call(this);
6|             });
6|         }
6|     };
6|     let copyProps = (target, source) => {
6|         Object.getOwnPropertyNames(source)
6|             .concat(Object.getOwnPropertySymbols(source))
6|             .forEach((prop) => {
6|             if (prop.match(/^(?:constructor@3@prototype@3@arguments@3@caller@3@name@3@bind@3@call@3@apply@3@toString@3@length)$/))
6|                 return
6|             Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
6|         })
6|     }
6|     mixins.forEach((mixin) => {
6|         copyProps(base.prototype, mixin.prototype);
6|         copyProps(base, mixin);
6|     });
6|     return base;
6| };
6|
6| class |Colored| {
6|     |initializer| ()     { this._color = "white"; }
6|     get color ()       { return this._color; }
6|     set color (v)      { this._color = v; }
6| }
6|
6| class |ZCoord| {
6|     |initializer| ()     { this._z = 0; }
6|     get z ()           { return this._z; }
6|     set z (v)          { this._z = v; }
6| }
6|
6| class |Shape| {
6|     |constructor| (x, y) { this._x = x; this._y = y; }
6|     get x ()           { return this._x; }
6|     set x (v)          { this._x = v; }
6|     get y ()           { return this._y; }
6|     set y (v)          { this._y = v; }
6| }
6|
6| class Rectangle extends |aggregation(Shape, Colored, ZCoord)| {}
6|
6| var rect = new Rectangle(7, 42);
6| rect.|z|     = 1000;
6| rect.|color| = "red";
6| console.log(rect.x, rect.y, rect.z, rect.color);

5| var |aggregation| = function (baseClass, mixins) {
5|     var base = function () {
5|         |baseClass.apply|(this, arguments);
5|         mixins.forEach(function (mixin) {
5|             mixin.prototype.|initializer|.call(this);
5|         }.bind(this));
5|     };
5|     base.prototype = Object.create(baseClass.prototype);
5|     base.prototype.constructor = base;
5|     var copyProps = function (target, source) {
5|         Object.getOwnPropertyNames(source).forEach(function (prop) {
5|             if (prop.match(/^(?:constructor@3@prototype@3@arguments@3@caller@3@name@3@bind@3@call@3@apply@3@toString@3@length)$/))
5|                 return
5|             Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
5|         })
5|     }
5|     mixins.forEach(function (mixin) {
5|         copyProps(base.prototype, mixin.prototype);
5|         copyProps(base, mixin);
5|     });
5|     return base;
5| };
5|
5| var Colored = function () {};
5| Colored.prototype = {
5|     |initializer|: function ()  { this._color = "white"; },
5|     getColor:    function ()  { return this._color; },
5|     setColor:    function (v) { this._color = v; }
5| };
5|
5| var ZCoord = function () {};
5| ZCoord.prototype = {
5|     |initializer|: function ()  { this._z = 0; },
5|     getZ:        function ()  { return this._z; },
5|     setZ:        function (v) { this._z = v; }
5| };
5|
5| var Shape = function (x, y) {
5|     this._x = x; this._y = y;
5| };
5| Shape.prototype = {
5|     getX: function ()  { return this._x; },
5|     setX: function (v) { this._x = v; },
5|     getY: function ()  { return this._y; },
5|     setY: function (v) { this._y = v; }
5| }
5|
5| var _Combined = |aggregation(Shape, [ Colored, ZCoord ])|;
5| var Rectangle = function (x, y) {
5|     _Combined.call(this, x, y);
5| };
5| Rectangle.prototype = Object.create(_Combined.prototype);
5| Rectangle.prototype.constructor = Rectangle;
5|
5| var rect = new Rectangle(7, 42);
5| rect.|setZ|(1000);
5| rect.|setColor|("red");
5| console.log(rect.getX(), rect.getY(), rect.getZ(), rect.getColor());

Base Class Access
-----------------

Intuitive access to base class constructor and methods.

6| class Shape {
6|     ...
6|     |toString| () {
6|         return `Shape(${this.id})`
6|     }
6| }
6| class Rectangle extends Shape {
6|     constructor (id, x, y, width, height) {
6|         |super|(id, x, y);
6|         ...
6|     }
6|     |toString| () {
6|         return "Rectangle > " + |super.toString()|;
6|     }
6| }
6| class Circle extends Shape {
6|     constructor (id, x, y, radius) {
6|         |super|(id, x, y);
6|         ...
6|     }
6|     |toString| () {
6|         return "Circle > " + |super.toString()|;
6|     }
6| }

5| var Shape = |function| (id, x, y) {
5|     ...
5| };
5| Shape.|prototype.toString| = function (x, y) {
5|     return "Shape(" + this.id + ")"
5| };
5| var Rectangle = function (id, x, y, width, height) {
5|     |Shape.call|(this, id, x, y);
5|     ...
5| };
5| Rectangle|.prototype.toString| = function () {
5|     return "Rectangle > " + |Shape.prototype.toString.call(this)|;
5| };
5| var Circle = function (id, x, y, radius) {
5|     |Shape.call|(this, id, x, y);
5|     ...
5| };
5| Circle|.prototype.toString| = function () {
5|     return "Circle > " + |Shape.prototype.toString.call(this)|;
5| };

Static Members
--------------

Simple support for static class members.

6| class Rectangle extends Shape {
6|     ...
6|     |static defaultRectangle| () {
6|         return new Rectangle("default", 0, 0, 100, 100);
6|     }
6| }
6| class Circle extends Shape {
6|     ...
6|     |static defaultCircle| () {
6|         return new Circle("default", 0, 0, 100);
6|     }
6| }
6| var defRectangle = |Rectangle.defaultRectangle()|;
6| var defCircle    = |Circle.defaultCircle()|;

5| var Rectangle = function (id, x, y, width, height) {
5|     ...
5| };
5| |Rectangle.defaultRectangle| = function () {
5|     return new Rectangle("default", 0, 0, 100, 100);
5| };
5| var Circle = function (id, x, y, width, height) {
5|     ...
5| };
5| |Circle.defaultCircle| = function () {
5|     return new Circle("default", 0, 0, 100);
5| };
5| var defRectangle = |Rectangle.defaultRectangle()|;
5| var defCircle    = |Circle.defaultCircle()|;

Getter/Setter
-------------

Getter/Setter also directly within classes (and not just within object
initializers, as it is possible since ECMAScript 5.1).

6| class Rectangle {
6|     constructor (width, height) {
6|         this.width  = width;
6|         this.height = height;
6|     }
6|     |set| width  (width)  { this._width = width;             }
6|     |get| width  ()       { return this._width;              }
6|     |set| height (height) { this._height = height;           }
6|     |get| height ()       { return this._height;             }
6|     |get| area   ()       { return this.width * this.height; }
6| };
6| var r = new Rectangle(50, 20);
6| r.area === 1000;

5| var Rectangle = function (width, height) {
5|     this.width  = width;
5|     this.height = height;
5| };
5| Rectangle|.prototype| = {
5|     |set| width  (width)  { this._width = width;             }|,|
5|     |get| width  ()       { return this._width;              }|,|
5|     |set| height (height) { this._height = height;           }|,|
5|     |get| height ()       { return this._height;             }|,|
5|     |get| area   ()       { return this.width * this.height; }
5| };
5| var r = new Rectangle(50, 20);
5| r.area === 1000;


Symbol Type
===========

Symbol Type
-----------

Unique and immutable data type to be used as an identifier for object properties.
Symbol can have an optional description, but for debugging purposes only.

6| Symbol("foo") |!==| Symbol("foo");
6| const foo = Symbol();
6| const bar = Symbol();
6| typeof foo === |"symbol"|;
6| typeof bar === |"symbol"|;
6| let obj = {};
6| obj[foo] = "foo";
6| obj[bar] = "bar";
6| JSON.stringify(obj); // {}
6| Object.keys(obj); // []
6| Object.getOwnPropertyNames(obj); // []
6| Object.|getOwnPropertySymbols|(obj); // [ foo, bar ]

5| // no equivalent in ES5

Global Symbols
--------------

Global symbols, indexed through unique keys.

6| Symbol.for("app.foo") |===| Symbol.for("app.foo")
6| const foo = Symbol.for("app.foo");
6| const bar = Symbol.for("app.bar");
6| Symbol.keyFor(foo) === "app.foo";
6| Symbol.keyFor(bar) === "app.bar";
6| typeof foo === |"symbol"|;
6| typeof bar === |"symbol"|;
6| let obj = {};
6| obj[foo] = "foo";
6| obj[bar] = "bar";
6| JSON.stringify(obj); // {}
6| Object.keys(obj); // []
6| Object.getOwnPropertyNames(obj); // []
6| Object.|getOwnPropertySymbols|(obj); // [ foo, bar ]

5| // no equivalent in ES5


Iterators
=========

Iterator & For-Of Operator
--------------------------

Support "iterable" protocol to allow objects to customize their
iteration behaviour. Additionally, support "iterator" protocol to produce
sequence of values (either finite or infinite). Finally, provide
convenient `of` operator to iterate over all values of an iterable object.

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

5| var fibonacci = {
5|     |next:| ((function () {
5|         var pre = 0, cur = 1;
5|         return function () {
5|             tmp = pre;
5|             pre = cur;
5|             cur += tmp;
5|             return cur;
5|         };
5|     })();
5| };
5|
5| var n;
5| for (;;) {
5|     n = fibonacci|.next()|;
5|     if (n > 1000)
5|         break;
5|     console.log(n);
5| }

Generators
==========

Generator Function, Iterator Protocol
-------------------------------------

Support for generators, a special case of Iterators containing a
generator function, where the control flow can be paused and resumed, in
order to produce sequence of values (either finite or infinite).

6| let fibonacci = {
6|     |*[Symbol.iterator]()| {
6|         let pre = 0, cur = 1;
6|         |for (;;)| {
6|             [ pre, cur ] = [ cur, pre + cur ];
6|             |yield cur|;
6|         }
6|     }
6| }
6|
6| for (|let n of fibonacci|) {
6|     if (n > 1000)
6|         break;
6|     console.log(n);
6| }

5| var fibonacci = {
5|     next: ((|function| () {
5|         var pre = 0, cur = 1;
5|         return function () {
5|             tmp = pre;
5|             pre = cur;
5|             cur += tmp;
5|             return cur;
5|         };
5|     })();
5| };
5|
5| var n;
5| for (;;) {
5|     n = fibonacci|.next()|;
5|     if (n > 1000)
5|         break;
5|     console.log(n);
5| }

Generator Function, Direct Use
------------------------------

Support for generator functions, a special variant of functions where
the control flow can be paused and resumed, in order to produce sequence
of values (either finite or infinite).

6| |function*| range (start, end, step) {
6|     while (start < end) {
6|         |yield| start;
6|         start += step;
6|     }
6| }
6|
6| for (let i |of| range(0, 10, 2)) {
6|     console.log(i); // 0, 2, 4, 6, 8
6| }

5| |function| range (start, end, step) {
5|     |var list = []|;
5|     while (start < end) {
5|         |list.push(start)|;
5|         start += step;
5|     }
5|     |return list|;
5| }
5|
5| |var r = range(0, 10, 2)|;
5| for (|var i = 0; i < r.length; i++|) {
5|     console.log(|r[i]|); // 0, 2, 4, 6, 8
5| }

Generator Matching
------------------

Support for generator functions, i.e., functions where the control flow
can be paused and resumed, in order to produce and spread sequence of
values (either finite or infinite).

6| let fibonacci = |function*| (numbers) {
6|     let pre = 0, cur = 1;
6|     while (numbers-- > 0) {
6|         [ pre, cur ] = [ cur, pre + cur ];
6|         |yield cur|;
6|     }
6| };
6|
6| for (|let n of fibonacci(1000)|)
6|     console.log(n);
6|
6| let numbers = [ |...fibonacci(1000)| ];
6|
6| let [ n1, n2, n3, ...others ] = |fibonacci(1000)|;

5| //  no equivalent in ES5


Generator Control-Flow
----------------------

Support for generators, a special case of Iterators where the control
flow can be paused and resumed, in order to support asynchronous
programming in the style of "co-routines" in combination with Promises (see below).
[Notice: the generic `async` function usually is provided by a reusable library
and given here just for better understanding. See
[co](https://github.com/tj/co) or
[Bluebird](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisecoroutinegeneratorfunction-generatorfunction---function)'s `coroutine` in practice.]

6| //  generic asynchronous control-flow driver
6| function |async| (proc, ...params) {
6|     var |iterator| = |proc(...params)|;
6|     return new Promise((resolve, reject) => {
6|         let loop = (value) => {
6|             let result;
6|             try {
6|                 result = |iterator.next|(value);
6|             }
6|             catch (err) {
6|                 reject(err);
6|             }
6|             if (result.done)
6|                 resolve(result.value);
6|             else if (   typeof result.value      === "object"
6|                      && typeof result.value.then === "function")
6|                 result.value.then((value) => {
6|                     loop(value);
6|                 }, (err) => {
6|                     reject(err);
6|                 });
6|             else
6|                 loop(result.value);
6|         }
6|         loop();
6|     });
6| }
6|
6| //  application-specific asynchronous builder
6| function makeAsync (text, after) {
6|     return new Promise((resolve, reject) => {
6|         setTimeout(() => resolve(text), after);
6|     });
6| }
6|
6| //  application-specific asynchronous procedure
6| |async|(|function*| (greeting) {
6|     let foo = |yield| makeAsync("foo", 300)
6|     let bar = |yield| makeAsync("bar", 200)
6|     let baz = |yield| makeAsync("baz", 100)
6|     return `${greeting} ${foo} ${bar} ${baz}`
6| }, "Hello").then((msg) => {
6|     console.log("RESULT:", msg); // "Hello foo bar baz"
6| })

5| //  no equivalent in ES5


# Comprehensions
# ==============
#
# List Comprehension
# ------------------
#
# Concise FP-style way to create a list/array, based on another list where
# each entry is the result of some operation.
#
# 6| let results = [
# 6|     |for (c |of| customers)|
# 6|         if (c.city === "Munich")
# 6|             { name: c.name, age: c.age } ];
#
# 5| var results = |[]|;
# 5| for (var c |in| customers)
# 5|     if (customers.hasOwnProperty(c))
# 5|         if (c.city === "Munich")
# 5|             |result.push|({ name: c.name, age: c.age });
#
# Generator Comprehension
# -----------------------
#
# Concise FP-style way to create an object, based on another object where
# each entry is the result of some operation.
#
# 6| let results = (
# 6|     for (|c of customers|)
# 6|         if (c.city == "Munich")
# 6|             { name: c.name, age: c.age } );
#
# # 5| var results = |[]|;
# 5| for (|var c = customers.next(); !c.done; c = customers.next()|)
# 5|     if (c.city === "Munich")
# 5|         |result.push|({ name: c.name, age: c.age });


Map/Set & WeakMap/WeakSet
=========================

Set Data-Structure
------------------

Cleaner data-structure for common algorithms based on sets.

6| let s = |new Set()|;
6| s.|add("hello")|.|add("goodbye")|.|add("hello")|;
6| s.|size| === 2;
6| s.|has("hello")| === true;
6| for (let key |of| s.|values()|) // insertion order
6|     console.log(key);

5| var s = |{}|;
5| |s["hello"] = true|; |s["goodbye"] = true|; |s["hello"] = true|;
5| |Object.keys(s).length| === 2;
5| |s["hello"]| === true;
5| for (var key |in| s) // arbitrary order
5|     if (s.hasOwnProperty(key))
5|         console.log(s[key]);

Map Data-Structure
------------------

Cleaner data-structure for common algorithms based on maps.

6| let m = |new Map()|;
6| m.|set("hello", 42)|;
6| m.|set(s, 34)|;
6| m.|get(s)| === 34;
6| m.|size| === 2;
6| for (let |[ key, val ] of m.entries()|)
6|     console.log(key + " = " + val);

5| var m = |{}|;
5| |m["hello"] = 42|;
5| // no equivalent in ES5
5| // no equivalent in ES5
5| |Object.keys(m).length| === 2;
5| for (key |in| m) {
5|     if (m.hasOwnProperty(key)) {
5|         var |val = m[key]|;
5|         console.log(key + " = " + val);
5|     }
5| }


Weak-Link Data-Structures
-------------------------

Memory-leak-free Object-key’d side-by-side data-structures.

6| let isMarked     = new |WeakSet|();
6| let attachedData = new |WeakMap|();
6|
6| export class Node {
6|     constructor (id)   { this.id = id;                  }
6|     mark        ()     { |isMarked.add|(this);            }
6|     unmark      ()     { |isMarked.delete|(this);         }
6|     marked      ()     { return |isMarked.has|(this);     }
6|     set data    (data) { |attachedData.set|(this, data);  }
6|     get data    ()     { return |attachedData.get|(this); }
6| }
6|
6| new foo = new Node("foo");
6| JSON.stringify(foo) === |'{"id":"foo"}'|;
6| foo.mark();
6| foo.data = "bar";
6| foo.data === "bar";
6| JSON.stringify(foo) === |'{"id":"foo"}'|;

5| // no equivalent in ES5


Typed Arrays
============

Typed Arrays
------------

Support for arbitrary byte-based data structures to implement network
protocols, cryptography algorithms, file format manipulations, etc.

6| //  ES6 class equivalent to the following C structure:
6| //  struct Example { unsigned long id; char username[16]; float amountDue; };
6| class Example {
6|     constructor (buffer = |new ArrayBuffer(24)|) {
6|         this.buffer = buffer;
6|     }
6|     set buffer (buffer) {
6|         this._buffer    = buffer;
6|         this._id        = |new Uint32Array (this._buffer,  0,  1)|;
6|         this._username  = |new Uint8Array  (this._buffer,  4, 16)|;
6|         this._amountDue = |new Float32Array(this._buffer, 20,  1)|;
6|     }
6|     get buffer ()     { return this._buffer;       }
6|     set id (v)        { this._id[0] = v;           }
6|     get id ()         { return this._id[0];        }
6|     set username (v)  { this._username[0] = v;     }
6|     get username ()   { return this._username[0];  }
6|     set amountDue (v) { this._amountDue[0] = v;    }
6|     get amountDue ()  { return this._amountDue[0]; }
6| }
6|
6| let example = new Example()
6| example.|id| = 7
6| example.|username| = "John Doe"
6| example.|amountDue| = 42.0

5| //  no equivalent in ES5
5| //  (only an equivalent in HTML5)


New Built-In Methods
====================

Object Property Assignment
--------------------------

New function for assigning enumerable properties of one or more source
objects onto a destination object.

6| var dst  = { quux: 0 };
6| var src1 = { foo: |1|, bar: 2 };
6| var src2 = { foo: |3|, baz: 4 };
6| Object|.assign|(dst, src1, src2);
6| dst.quux === 0;
6| dst.foo  === |3|;
6| dst.bar  === 2;
6| dst.baz  === 4;

5| var dst  = { quux: 0 };
5| var src1 = { foo: |1|, bar: 2 };
5| var src2 = { foo: |3|, baz: 4 };
5| Object|.keys(src1).forEach|(function(k) {
5|     |dst[k] = src1[k]|;
5| });
5| Object|.keys(src2).forEach|(function(e) {
5|     |dst[k] = src2[k]|;
5| });
5| dst.quux === 0;
5| dst.foo  === |3|;
5| dst.bar  === 2;
5| dst.baz  === 4;

Array Element Finding
---------------------

New function for finding an element in an array.

6| [ 1, 3, 4, 2 ].|find|(|x =>| x > 3); // 4

5| [ 1, 3, 4, 2 ].|filter|(|function (x) { return| x > 3; })|[0]|; // 4

String Repeating
----------------

New string repeating functionality.

6| " ".|repeat|(4 * depth);
6| "foo".|repeat|(3);

5| |Array|((4 * depth) + 1).|join|(" ");
5| |Array|(3 + 1).|join|("foo");

String Searching
----------------

New specific string functions to search for a sub-string.

6| "hello".|startsWith|("ello", 1); // true
6| "hello".|endsWith|("hell", 4);   // true
6| "hello".|includes|("ell");       // true
6| "hello".|includes|("ell", 1);    // true
6| "hello".|includes|("ell", 2);    // false

5| "hello".|indexOf|("ello") === 1;    // true
5| "hello".|indexOf|("hell") === (4 |- "hell".length|); // true
5| "hello".|indexOf|("ell") !== -1;    // true
5| "hello".|indexOf|("ell", 1) !== -1; // true
5| "hello".|indexOf|("ell", 2) !== -1; // false

Number Type Checking
--------------------

New functions for checking for non-numbers and finite numbers.

6| Number.|isNaN|(42) === false;
6| Number.|isNaN|(NaN) === true;
6|
6| Number.|isFinite|(Infinity) === false;
6| Number.|isFinite|(-Infinity) === false;
6| Number.|isFinite|(NaN) === false;
6| Number.|isFinite|(123) === true;

5| var |isNaN| = function (n) {
5|     return |n !== n|;
5| };
5| var |isFinite| = function (v) {
5|     return |(typeof v === "number" && !isNaN(v) && v !== Infinity && v !== -Infinity)|;
5| };
5| isNaN(42) === false;
5| isNaN(NaN) === true;
5|
5| isFinite(Infinity) === false;
5| isFinite(-Infinity) === false;
5| isFinite(NaN) === false;
5| isFinite(123) === true;

Number Safety Checking
----------------------

Checking whether an integer number is in the safe range, i.e., it is
correctly represented by JavaScript (where all numbers, including
integer numbers, are technically floating point number).

6| Number.|isSafeInteger|(42) === true;
6| Number.|isSafeInteger|(9007199254740992) === false;

5| function isSafeInteger (n) {
5|     return (
5|            typeof n === 'number'
5|         && Math.round(n) === n
5|         && -(Math.pow(2, 53) - 1) <= n
5|         && n <= (Math.pow(2, 53) - 1)
5|     );
5| }
5| |isSafeInteger|(42) === true;
5| |isSafeInteger|(9007199254740992) === false;

Number Comparison
-----------------

Availability of a standard Epsilon value for more precise comparison of
floating point numbers.

6| console.log(0.1 + 0.2 === 0.3); // false
6| console.log(Math.abs((0.1 + 0.2) - 0.3) < |Number.EPSILON|); // true

5| console.log(0.1 + 0.2 === 0.3); // false
5| console.log(Math.abs((0.1 + 0.2) - 0.3) < |2.220446049250313e-16|); // true

Number Truncation
-----------------

Truncate a floating point number to its integral part,
completely dropping the fractional part.

6| console.log(|Math.trunc|(42.7)) // 42
6| console.log(|Math.trunc|( 0.1)) // 0
6| console.log(|Math.trunc|(-0.1)) // -0

5| function mathTrunc (x) {
5|     return |(x < 0 ? Math.ceil(x) : Math.floor(x))|;
5| }
5| console.log(mathTrunc(42.7)) // 42
5| console.log(mathTrunc( 0.1)) // 0
5| console.log(mathTrunc(-0.1)) // -0

Number Sign Determination
-------------------------

Determine the sign of a number, including special cases of signed zero and non-number.

6| console.log(|Math.sign|(7))   // 1
6| console.log(|Math.sign|(0))   // 0
6| console.log(|Math.sign|(-0))  // -0
6| console.log(|Math.sign|(-7))  // -1
6| console.log(|Math.sign|(NaN)) // NaN

5| function |mathSign| (x) {
5|     return |((x === 0 || isNaN(x)) ? x : (x > 0 ? 1 : -1))|;
5| }
5| console.log(mathSign(7))   // 1
5| console.log(mathSign(0))   // 0
5| console.log(mathSign(-0))  // -0
5| console.log(mathSign(-7))  // -1
5| console.log(mathSign(NaN)) // NaN


Promises
========

Promise Usage
-------------

First class representation of a value that may be made asynchronously and be available in the future.

6| function msgAfterTimeout (msg, who, timeout) {
6|     return |new Promise|((|resolve|, reject) => {
6|         setTimeout(() => |resolve|(`${msg} Hello ${who}!`), timeout);
6|     });
6| }
6| msgAfterTimeout("", "Foo", 100).|then|((msg) =>
6|     msgAfterTimeout(msg, "Bar", 200);
6| ).|then|((msg) => {
6|     console.log(`done after 300ms:${msg}`);
6| });

5| function msgAfterTimeout (msg, who, timeout, |onDone|) {
5|     setTimeout(function () {
5|         |onDone|(msg + " Hello " + who + "!");
5|     }, timeout);
5| }
5| msgAfterTimeout("", "Foo", 100|, function| (msg) {
5|     msgAfterTimeout(msg, "Bar", 200|, function| (msg) {
5|         console.log("done after 300ms:" + msg);
5|     });
5| });

Promise Combination
-------------------

Combine one or more promises into new promises without having to take
care of ordering of the underlying asynchronous operations yourself.

6| function fetchAsync (url, timeout, onData, onError) {
6|     ...
6| }
6| let fetchPromised = (url, timeout) => {
6|     return |new Promise|((|resolve|, |reject|) => {
6|         fetchAsync(url, timeout, |resolve|, |reject|);
6|     });
6| }
6| |Promise.all|([
6|     fetchPromised("http://backend/foo.txt", 500),
6|     fetchPromised("http://backend/bar.txt", 500),
6|     fetchPromised("http://backend/baz.txt", 500)
6| ]).|then|((data) => {
6|     let [ foo, bar, baz ] = data;
6|     console.log(`success: foo=${foo} bar=${bar} baz=${baz}`);
6| }, (err) => {
6|     console.log(`error: ${err}`);
6| });

5| function fetchAsync (url, timeout, onData, onError) {
5|     ...
5| }
5| function |fetchAll| (request, onData, onError) {
5|     var result = [], results = 0;
5|     for (var i = 0; i < request.length; i++) {
5|         result[i] = null;
5|         (function (i) {
5|             fetchAsync(request[i].url, request[i].timeout, function (data) {
5|                 |result[i] = data|;
5|                 |if (++results === request.length)|
5|                     |onData(result)|;
5|             }, onError);
5|         })(i);
5|     }
5| }
5| |fetchAll|([
5|     { url: "http://backend/foo.txt", timeout: 500 },
5|     { url: "http://backend/bar.txt", timeout: 500 },
5|     { url: "http://backend/baz.txt", timeout: 500 }
5| ], function (data) {
5|     var foo = data[0], bar = data[0], baz = data[0];
5|     console.log("success: foo=" + foo + " bar=" + bar + " baz=" + baz);
5| }, function (err) {
5|     console.log("error: " + err);
5| });


Meta-Programming
================

Proxying
--------

Hooking into runtime-level object meta-operations.

6| let target = {
6|     foo: "Welcome, foo"
6| };
6| let proxy = |new Proxy|(|target|, {
6|     |get (receiver, name)| {
6|         return name in receiver ? receiver[name] : `Hello, ${name}`;
6|     }
6| });
6| proxy.foo   === "Welcome, foo";
6| proxy.|world| === "Hello, |world|";

5| // no equivalent in ES5

Reflection
----------

Make calls corresponding to the object meta-operations.

6| let obj = { a: 1 };
6| Object.defineProperty(obj, "b", { value: 2 });
6| obj[Symbol("c")] = 3;
6| |Reflect.ownKeys|(obj); // [ "a", "b", Symbol(c) ]

5| var obj = { a: 1 };
5| Object.defineProperty(obj, "b", { value: 2 });
5| // no equivalent in ES5
5| |Object.getOwnPropertyNames|(obj); // [ "a", "b" ]

<commit_msg>Fixed array indices typo in Promise Combination<commit_after>##
##  es6-features -- ECMAScript 6 Feature Overview & Comparison
##  Copyright (c) 2015 <NAME> <<EMAIL>>
##
##  Permission is hereby granted, free of charge, to any person obtaining
##  a copy of this software and associated documentation files (the
##  "Software"), to deal in the Software without restriction, including
##  without limitation the rights to use, copy, modify, merge, publish,
##  distribute, sublicense, and/or sell copies of the Software, and to
##  permit persons to whom the Software is furnished to do so, subject to
##  the following conditions:
##
##  The above copyright notice and this permission notice shall be included
##  in all copies or substantial portions of the Software.
##
##  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
##  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
##  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
##  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
##  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
##  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
##  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##


Constants
=========

Constants
---------

Support for constants (immutable variables).

6| |const PI| = 3.141593;
6| PI > 3.0;

5| //  only in ES5 through the help of object properties
5| //  and only in global context and not in a block scope
5| Object.|defineProperty|(typeof global === "object" ? global : window, "|PI|", {
5|     value:        3.141593,
5|     enumerable:   true,
5|     |writable:     false|,
5|     |configurable: false|
5| })
5| PI > 3.0;


Scoping
=======

Block-Scoped Variables
----------------------

Block-scoped variables (and constants) without hoisting.

6| for (|let i| = 0; i < a.length; i++) {
6|     |let x| = a[i];
6|     ...
6| }
6| for (|let i| = 0; i < b.length; i++) {
6|     |let y| = b[i];
6|     ...
6| }
6|
6| let callbacks = [];
6| for (|let| i = 0; i <= 2; i++) {
6|     callbacks[i] = function () { return i * 2; };
6| }
6| callbacks[0]() === 0;
6| callbacks[1]() === 2;
6| callbacks[2]() === 4;

5| |var i, x, y|;
5| for (i = 0; i < a.length; i++) {
5|     x = a[i];
5|     ...
5| }
5| for (i = 0; i < b.length; i++)
5|     y = b[i];
5|     ...
5| }
5|
5| var callbacks = [];
5| for (|var| i = 0; i <= 2; i++) {
5|     |(function (i) {|
5|         callbacks[i] = function() { return i * 2; };
5|     |})(i);|
5| }
5| callbacks[0]() === 0;
5| callbacks[1]() === 2;
5| callbacks[2]() === 4;


Block-Scoped Functions
----------------------

Block-scoped function definitions.

6| {
6|     function foo () { return 1; }
6|     foo() === 1;
6|     {
6|         function |foo| () { return |2|; }
6|         |foo|() === |2|;
6|     }
6|     foo() === 1;
6| }

5| //  only in ES5 with the help of block-scope emulating
5| //  function scopes and function expressions
5| (function () {
5|     var foo = function () { return 1; }
5|     foo() === 1;
5|     |(function () {|
5|         var |foo| = function () { return |2|; }
5|         |foo|() === |2|;
5|     |})();|
5|     foo() === 1;
5| })();

Arrow Functions
===============

Expression Bodies
-----------------

More expressive closure syntax.

6| odds  = evens.map(|v =>| v + 1);
6| pairs = evens.map(|v => (|{ even: v, odd: v + 1 }|)|);
6| nums  = evens.map(|(v, i) =>| v + i);

5| odds  = evens.map(|function (v) { return| v + 1; |}|);
5| pairs = evens.map(|function (v) { return| { even: v, odd: v + 1 }; |}|);
5| nums  = evens.map(|function (v, i) { return| v + i; |}|);

Statement Bodies
----------------

More expressive closure syntax.

6| nums.forEach(|v => {|
6|    if (v % 5 === 0)
6|        fives.push(v);
6| |}|)

5| nums.forEach(|function (v) {|
5|    if (v % 5 === 0)
5|        fives.push(v);
5| |}|);

Lexical `this`
--------------

More intuitive handling of current object context.

6| |this|.nums.forEach(|(v) =>| {
6|     if (v % 5 === 0)
6|         |this|.fives.push(v);
6| });

5| //  variant 1
5| var |self| = |this|;
5| |this|.nums.forEach(|function (v)| {
5|     if (v % 5 === 0)
5|         |self|.fives.push(v);
5| });
5|
5| //  variant 2 (since ECMAScript 5.1 only)
5| |this|.nums.forEach(|function (v)| {
5|     if (v % 5 === 0)
5|         |this|.fives.push(v);
5| }|.bind(this)|);


Extended Parameter Handling
===========================

Default Parameter Values
------------------------

Simple and intuitive default values for function parameters.

6| function f (x, y |= 7|, z |= 42|) {
6|     return x + y + z;
6| }
6| f(1) === 50;

5| function f (x, y, z) {
5|     |if (y === undefined)|
5|         |y = 7|;
5|     |if (z === undefined)|
5|         |z = 42|;
5|     return x + y + z;
5| };
5| f(1) === 50;

Rest Parameter
--------------

Aggregation of remaining arguments into single parameter of variadic functions.

6| function f (x, y, |...a|) {
6|     return (x + y) * a.length;
6| }
6| f(1, 2, |"hello", true, 7|) === 9;

5| function f (x, y) {
5|     |var a = Array.prototype.slice.call(arguments, 2)|;
5|     return (x + y) * a.length;
5| };
5| f(1, 2, |"hello", true, 7|) === 9;

Spread Operator
---------------

Spreading of elements of an interable collection (like an array or even
a string) into both literal elements and individual function parameters.

6| var params = [ "hello", true, 7 ];
6| var other = [ 1, 2, |...params| ]; // [ 1, 2, "hello", true, 7 ]
6| f(1, 2, |...params|) === 9;
6|
6| var str = "foo";
6| var chars = [ ... str ]; // [ "f", "o", "o" ]

5| var params = [ "hello", true, 7 ];
5| var other = [ 1, 2 ].|concat(params)|; // [ 1, 2, "hello", true, 7 ]
5| f|.apply|(|undefined, [| 1, 2 |].concat(params)|) === 9;
5|
5| var str = "foo";
5| var chars = str.split(""); // [ "f", "o", "o" ]

Template Strings
================

String Interpolation
--------------------

Intuitive expression interpolation for single-line and multi-line strings.

6| var customer = { name: "Foo" };
6| var card = { amount: 7, product: "Bar", unitprice: 42 };
6| message = `Hello |${customer.name}|,
6| want to buy |${card.amount} ${card.product}| for
6| a total of |${card.amount * card.unitprice}| bucks?`;

5| var customer = { name: "Foo" };
5| var card = { amount: 7, product: "Bar", unitprice: 42 };
5| message = "Hello "| + customer.name + |",\n" +
5| "want to buy "| + card.amount + |" "| + card.product + |" for\n" +
5| "a total of "| + (card.amount * card.unitprice) + |" bucks?";

Custom Interpolation
--------------------

Flexible expression interpolation for arbitrary methods.

6| get`http://example.com/foo?bar=|${|bar + baz|}|&quux=|${|quux|}|`;

5| get|([ |"http://example.com/foo?bar="|,| "&quux="|,| ""| ],|bar + baz|,| quux|)|;

Raw String Access
-----------------

Access the raw template string content (backslashes are not interpreted).

6| function quux (strings, ...values) {
6|     strings[0] === "|foo\n|";
6|     strings[1] === "bar";
6|     strings|.raw|[0] === "|foo\\n|";
6|     strings.raw[1] === "bar";
6|     values[0] === 42;
6| }
6| quux `foo|\n|${ 42 }bar`
6|
6| String.raw `foo|\n|${ 42 }bar` === "foo|\\n|42bar";

5| //  no equivalent in ES5


Extended Literals
=================

Binary & Octal Literal
----------------------

Direct support for safe binary and octal literals.

6| |0b|111110111 === 503;
6| |0o|767 === 503;

5| |parseInt(|"111110111"|, 2)| === 503;
5| |parseInt(|"767"|, 8)| === 503;
5| |0|767 === 503; // only in non-strict, backward compatibility mode

Unicode String & RegExp Literal
-------------------------------

Extended support using Unicode within strings and regular expressions.

6| "𠮷".length === 2;
6| "𠮷".match(/./|u|)[0].length === 2;
6| "|\u{20BB7}|" === "𠮷" === "\uD842\uDFB7";
6| "𠮷".|codePointAt|(0) == 0x20BB7;
6| for (let codepoint |of| "𠮷") console.log(codepoint);

5| "𠮷".length === 2;
5| "𠮷".match(/(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/)[0].length === 2;
5| "𠮷" === "\uD842\uDFB7";
5| //  no equivalent in ES5
5| //  no equivalent in ES5


Enhanced Regular Expression
===========================

Regular Expression Sticky Matching
----------------------------------

Keep the matching position sticky between matches and
this way support efficient parsing of arbitrary long input strings,
even with an arbitrary number of distinct regular expressions.

6| let parser = (input, match) => {
6|     for (let pos = 0, lastPos = input.length; |pos < lastPos|; ) {
6|         for (let i = 0; i < match.length; i++) {
6|             |match[i].pattern.lastIndex = pos|;
6|             let found;
6|             if ((found = match[i].pattern.exec(input)) !== null) {
6|                 match[i].action(found);
6|                 |pos = match[i].pattern.lastIndex|;
6|                 break;
6|             }
6|         }
6|     }
6| }
6|
6| let report = (match) => {
6|     console.log(JSON.stringify(match));
6| };
6| parser("Foo 1 Bar 7 Baz 42", [
6|     { pattern: /^Foo\s+(\d+)/|y|, action: (match) => report(match) },
6|     { pattern: /^Bar\s+(\d+)/|y|, action: (match) => report(match) },
6|     { pattern: /^Baz\s+(\d+)/|y|, action: (match) => report(match) },
6|     { pattern: /^\s*/|y|,         action: (match) => {}            }
6| ]);

5| var parser = function (input, match) {
5|     for (var i, found, |inputTmp = input|; |inputTmp !== ""|; ) {
5|         for (i = 0; i < match.length; i++) {
5|             if ((found = match[i].pattern.exec(inputTmp)) !== null) {
5|                 match[i].action(found);
5|                 |inputTmp = inputTmp.substr(found[0].length)|;
5|                 break;
5|             }
5|         }
5|     }
5| }
5|
5| var report = function (match) {
5|     console.log(JSON.stringify(match));
5| };
5| parser("Foo 1 Bar 7 Baz 42", [
5|     { pattern: /^Foo\s+(\d+)/, action: function (match) { report(match); } },
5|     { pattern: /^Bar\s+(\d+)/, action: function (match) { report(match); } },
5|     { pattern: /^Baz\s+(\d+)/, action: function (match) { report(match); } },
5|     { pattern: /^\s*/,         action: function (match) {}                 }
5| ]);


Enhanced Object Properties
==========================

Property Shorthand
------------------

Shorter syntax for common object property definition idiom.

6| obj = { |x|, |y| };

5| obj = { |x: x|, |y: y| };

Computed Property Names
-----------------------

Support for computed names in object property definitions.

6| obj = {
6|     foo: "bar",
6|     |[| "prop_" + foo() |]:| 42
6| };

5| obj = {
5|     foo: "bar"
5| };
5| |obj[| "prop_" + foo() |] =| 42;

Method Properties
-----------------

Support for method notation in object property definitions,
for both regular functions and generator functions.

6| obj = {
6|     |foo (a, b)| {
6|         ...
6|     },
6|     |bar (x, y)| {
6|         ...
6|     },
6|     |*quux (x, y)| {
6|         ...
6|     }
6| };

5| obj = {
5|     foo|: function| (a, b) {
5|         ...
5|     },
5|     bar|: function| (x, y) {
5|         ...
5|     },
5|     //  quux: no equivalent in ES5
5| };


Destructuring Assignment
========================

Array Matching
--------------

Intuitive and flexible destructuring of Arrays into individual variables during assignment.

6| var list = [ 1, 2, 3 ];
6| var |[ a, , b ] = list|;
6| [ |b, a| ] = [ |a, b| ];

5| var list = [ 1, 2, 3 ];
5| var |a = list[0]|, |b = list[2]|;
5| var |tmp = a|; |a = b|; |b = tmp|;

Object Matching, Shorthand Notation
-----------------------------------

Intuitive and flexible destructuring of Objects into individual variables during assignment.

6| var |{ op, lhs, rhs }| = getASTNode();

5| var |tmp| = getASTNode();
5| var |op  = tmp.op|;
5| var |lhs = tmp.lhs|;
5| var |rhs = tmp.rhs|;

Object Matching, Deep Matching
------------------------------

Intuitive and flexible destructuring of Objects into individual variables during assignment.

6| var { op: a, |lhs: { op: b }|, rhs: c } = getASTNode();

5| var tmp = getASTNode();
5| var a = tmp.op;
5| var |b = tmp.lhs.op|;
5| var c = tmp.rhs;

Parameter Context Matching
--------------------------

Intuitive and flexible destructuring of Arrays and Objects into individual parameters during function calls.

6| function f (|[ name, val ]|) {
6|     console.log(name, val);
6| }
6| function g (|{ name: n, val: v }|) {
6|     console.log(n, v);
6| }
6| function h (|{ name, val }|) {
6|     console.log(name, val);
6| }
6| f([ "bar", 42 ]);
6| g({ name: "foo", val:  7 });
6| h({ name: "bar", val: 42 });

5| function f (|arg|) {
5|     |var name = arg[0]|;
5|     |var val  = arg[1]|;
5|     console.log(name, val);
5| };
5| function g (|arg|) {
5|     |var n = arg.name|;
5|     |var v = arg.val|;
5|     console.log(n, v);
5| };
5| function h (|arg|) {
5|     |var name = arg.name|;
5|     |var val  = arg.val|;
5|     console.log(name, val);
5| };
5| f([ "bar", 42 ]);
5| g({ name: "foo", val:  7 });
5| h({ name: "bar", val: 42 });

Fail-Soft Destructuring
-----------------------

Fail-soft destructuring, optionally with defaults.

6| var list = [ 7, 42 ];
6| var [ a = 1, b = 2, |c = 3|, |d| ] = list;
6| a === 7;
6| b === 42;
6| c === |3|;
6| d === |undefined|;

5| var list = [ 7, 42 ];
5| var a = typeof list[0] !== "undefined" ? list[0] : 1;
5| var b = typeof list[1] !== "undefined" ? list[1] : 2;
5| var c = typeof list[2] !== "undefined" ? list[2] : |3|;
5| var d = typeof list[3] !== "undefined" ? list[3] : |undefined|;
5| a === 7;
5| b === 42;
5| c === |3|;
5| d === |undefined|;


Modules
=======

Symbol Export/Import
--------------------

Support for exporting/importing symbols from/to modules without global namespace pollution.

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

5| //  lib/math.js
5| |LibMath = {}|;
5| |LibMath.sum| = function (x, y) { return x + y };
5| |LibMath.pi| = 3.141593;
5|
5| //  someApp.js
5| var |math = LibMath|;
5| console.log("2π = " + math.sum(math.pi, math.pi));
5|
5| //  otherApp.js
5| var |sum = LibMath.sum|, |pi = LibMath.pi|;
5| console.log("2π = " + sum(pi, pi));

Default & Wildcard
------------------

Marking a symbol as the default exported symbol and mass-mixin of symbols.

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

5| //  lib/mathplusplus.js
5| |LibMathPP = {}|;
5| |for (symbol in LibMath)|
5|     |if (LibMath.hasOwnProperty(symbol))|
5|         |LibMathPP[symbol] = LibMath[symbol]|;
5| |LibMathPP.e| = 2.71828182846;
5| |LibMathPP.exp| = function (x) { return Math.exp(x) };
5|
5| //  someApp.js
5| var |exp = LibMathPP.exp|, |pi = LibMathPP.pi|, |e = libMathPP.e|;
5| console.log("e^{π} = " + exp(pi));


Classes
=======

Class Definition
----------------

More intuitive, OOP-style and boilerplate-free classes.

6| |class| Shape {
6|     |constructor| (id, x, y) {
6|         this.id = id;
6|         this.move(x, y);
6|     }
6|     |move| (x, y) {
6|         this.x = x;
6|         this.y = y;
6|     }
6| }

5| var Shape = |function| (id, x, y) {
5|     this.id = id;
5|     this.move(x, y);
5| };
5| Shape.|prototype.move| = |function| (x, y) {
5|     this.x = x;
5|     this.y = y;
5| };

Class Inheritance
-----------------

More intuitive, OOP-style and boilerplate-free inheritance.

6| |class| Rectangle |extends| Shape {
6|     constructor (id, x, y, width, height) {
6|         |super|(id, x, y);
6|         this.width  = width;
6|         this.height = height;
6|     }
6| }
6| |class| Circle |extends| Shape {
6|     constructor (id, x, y, radius) {
6|         |super|(id, x, y);
6|         this.radius = radius;
6|     }
6| }

5| var Rectangle = function (id, x, y, width, height) {
5|     |Shape.call|(this, id, x, y);
5|     this.width  = width;
5|     this.height = height;
5| };
5| |Rectangle.prototype = Object.create(Shape.prototype)|;
5| |Rectangle.prototype.constructor = Rectangle|;
5| var Circle = function (id, x, y, radius) {
5|     |Shape.call|(this, id, x, y);
5|     this.radius = radius;
5| };
5| |Circle.prototype = Object.create(Shape.prototype)|;
5| |Circle.prototype.constructor = Circle|;

Class Inheritance, From Expressions
-----------------------------------

Support for mixin-style inheritance by extending from expressions yielding
function objects. [Notice: the generic `aggregation` function is usually provided
by a library like [this one](https://github.com/rse/aggregation), of course]

6| var |aggregation| = (baseClass, ...mixins) => {
6|     let base = class _Combined extends baseClass {
6|         constructor (...args) {
6|             |super|(...args);
6|             mixins.forEach((mixin) => {
6|                 mixin.prototype.|initializer|.call(this);
6|             });
6|         }
6|     };
6|     let copyProps = (target, source) => {
6|         Object.getOwnPropertyNames(source)
6|             .concat(Object.getOwnPropertySymbols(source))
6|             .forEach((prop) => {
6|             if (prop.match(/^(?:constructor@3@prototype@3@arguments@3@caller@3@name@3@bind@3@call@3@apply@3@toString@3@length)$/))
6|                 return
6|             Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
6|         })
6|     }
6|     mixins.forEach((mixin) => {
6|         copyProps(base.prototype, mixin.prototype);
6|         copyProps(base, mixin);
6|     });
6|     return base;
6| };
6|
6| class |Colored| {
6|     |initializer| ()     { this._color = "white"; }
6|     get color ()       { return this._color; }
6|     set color (v)      { this._color = v; }
6| }
6|
6| class |ZCoord| {
6|     |initializer| ()     { this._z = 0; }
6|     get z ()           { return this._z; }
6|     set z (v)          { this._z = v; }
6| }
6|
6| class |Shape| {
6|     |constructor| (x, y) { this._x = x; this._y = y; }
6|     get x ()           { return this._x; }
6|     set x (v)          { this._x = v; }
6|     get y ()           { return this._y; }
6|     set y (v)          { this._y = v; }
6| }
6|
6| class Rectangle extends |aggregation(Shape, Colored, ZCoord)| {}
6|
6| var rect = new Rectangle(7, 42);
6| rect.|z|     = 1000;
6| rect.|color| = "red";
6| console.log(rect.x, rect.y, rect.z, rect.color);

5| var |aggregation| = function (baseClass, mixins) {
5|     var base = function () {
5|         |baseClass.apply|(this, arguments);
5|         mixins.forEach(function (mixin) {
5|             mixin.prototype.|initializer|.call(this);
5|         }.bind(this));
5|     };
5|     base.prototype = Object.create(baseClass.prototype);
5|     base.prototype.constructor = base;
5|     var copyProps = function (target, source) {
5|         Object.getOwnPropertyNames(source).forEach(function (prop) {
5|             if (prop.match(/^(?:constructor@3@prototype@3@arguments@3@caller@3@name@3@bind@3@call@3@apply@3@toString@3@length)$/))
5|                 return
5|             Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
5|         })
5|     }
5|     mixins.forEach(function (mixin) {
5|         copyProps(base.prototype, mixin.prototype);
5|         copyProps(base, mixin);
5|     });
5|     return base;
5| };
5|
5| var Colored = function () {};
5| Colored.prototype = {
5|     |initializer|: function ()  { this._color = "white"; },
5|     getColor:    function ()  { return this._color; },
5|     setColor:    function (v) { this._color = v; }
5| };
5|
5| var ZCoord = function () {};
5| ZCoord.prototype = {
5|     |initializer|: function ()  { this._z = 0; },
5|     getZ:        function ()  { return this._z; },
5|     setZ:        function (v) { this._z = v; }
5| };
5|
5| var Shape = function (x, y) {
5|     this._x = x; this._y = y;
5| };
5| Shape.prototype = {
5|     getX: function ()  { return this._x; },
5|     setX: function (v) { this._x = v; },
5|     getY: function ()  { return this._y; },
5|     setY: function (v) { this._y = v; }
5| }
5|
5| var _Combined = |aggregation(Shape, [ Colored, ZCoord ])|;
5| var Rectangle = function (x, y) {
5|     _Combined.call(this, x, y);
5| };
5| Rectangle.prototype = Object.create(_Combined.prototype);
5| Rectangle.prototype.constructor = Rectangle;
5|
5| var rect = new Rectangle(7, 42);
5| rect.|setZ|(1000);
5| rect.|setColor|("red");
5| console.log(rect.getX(), rect.getY(), rect.getZ(), rect.getColor());

Base Class Access
-----------------

Intuitive access to base class constructor and methods.

6| class Shape {
6|     ...
6|     |toString| () {
6|         return `Shape(${this.id})`
6|     }
6| }
6| class Rectangle extends Shape {
6|     constructor (id, x, y, width, height) {
6|         |super|(id, x, y);
6|         ...
6|     }
6|     |toString| () {
6|         return "Rectangle > " + |super.toString()|;
6|     }
6| }
6| class Circle extends Shape {
6|     constructor (id, x, y, radius) {
6|         |super|(id, x, y);
6|         ...
6|     }
6|     |toString| () {
6|         return "Circle > " + |super.toString()|;
6|     }
6| }

5| var Shape = |function| (id, x, y) {
5|     ...
5| };
5| Shape.|prototype.toString| = function (x, y) {
5|     return "Shape(" + this.id + ")"
5| };
5| var Rectangle = function (id, x, y, width, height) {
5|     |Shape.call|(this, id, x, y);
5|     ...
5| };
5| Rectangle|.prototype.toString| = function () {
5|     return "Rectangle > " + |Shape.prototype.toString.call(this)|;
5| };
5| var Circle = function (id, x, y, radius) {
5|     |Shape.call|(this, id, x, y);
5|     ...
5| };
5| Circle|.prototype.toString| = function () {
5|     return "Circle > " + |Shape.prototype.toString.call(this)|;
5| };

Static Members
--------------

Simple support for static class members.

6| class Rectangle extends Shape {
6|     ...
6|     |static defaultRectangle| () {
6|         return new Rectangle("default", 0, 0, 100, 100);
6|     }
6| }
6| class Circle extends Shape {
6|     ...
6|     |static defaultCircle| () {
6|         return new Circle("default", 0, 0, 100);
6|     }
6| }
6| var defRectangle = |Rectangle.defaultRectangle()|;
6| var defCircle    = |Circle.defaultCircle()|;

5| var Rectangle = function (id, x, y, width, height) {
5|     ...
5| };
5| |Rectangle.defaultRectangle| = function () {
5|     return new Rectangle("default", 0, 0, 100, 100);
5| };
5| var Circle = function (id, x, y, width, height) {
5|     ...
5| };
5| |Circle.defaultCircle| = function () {
5|     return new Circle("default", 0, 0, 100);
5| };
5| var defRectangle = |Rectangle.defaultRectangle()|;
5| var defCircle    = |Circle.defaultCircle()|;

Getter/Setter
-------------

Getter/Setter also directly within classes (and not just within object
initializers, as it is possible since ECMAScript 5.1).

6| class Rectangle {
6|     constructor (width, height) {
6|         this.width  = width;
6|         this.height = height;
6|     }
6|     |set| width  (width)  { this._width = width;             }
6|     |get| width  ()       { return this._width;              }
6|     |set| height (height) { this._height = height;           }
6|     |get| height ()       { return this._height;             }
6|     |get| area   ()       { return this.width * this.height; }
6| };
6| var r = new Rectangle(50, 20);
6| r.area === 1000;

5| var Rectangle = function (width, height) {
5|     this.width  = width;
5|     this.height = height;
5| };
5| Rectangle|.prototype| = {
5|     |set| width  (width)  { this._width = width;             }|,|
5|     |get| width  ()       { return this._width;              }|,|
5|     |set| height (height) { this._height = height;           }|,|
5|     |get| height ()       { return this._height;             }|,|
5|     |get| area   ()       { return this.width * this.height; }
5| };
5| var r = new Rectangle(50, 20);
5| r.area === 1000;


Symbol Type
===========

Symbol Type
-----------

Unique and immutable data type to be used as an identifier for object properties.
Symbol can have an optional description, but for debugging purposes only.

6| Symbol("foo") |!==| Symbol("foo");
6| const foo = Symbol();
6| const bar = Symbol();
6| typeof foo === |"symbol"|;
6| typeof bar === |"symbol"|;
6| let obj = {};
6| obj[foo] = "foo";
6| obj[bar] = "bar";
6| JSON.stringify(obj); // {}
6| Object.keys(obj); // []
6| Object.getOwnPropertyNames(obj); // []
6| Object.|getOwnPropertySymbols|(obj); // [ foo, bar ]

5| // no equivalent in ES5

Global Symbols
--------------

Global symbols, indexed through unique keys.

6| Symbol.for("app.foo") |===| Symbol.for("app.foo")
6| const foo = Symbol.for("app.foo");
6| const bar = Symbol.for("app.bar");
6| Symbol.keyFor(foo) === "app.foo";
6| Symbol.keyFor(bar) === "app.bar";
6| typeof foo === |"symbol"|;
6| typeof bar === |"symbol"|;
6| let obj = {};
6| obj[foo] = "foo";
6| obj[bar] = "bar";
6| JSON.stringify(obj); // {}
6| Object.keys(obj); // []
6| Object.getOwnPropertyNames(obj); // []
6| Object.|getOwnPropertySymbols|(obj); // [ foo, bar ]

5| // no equivalent in ES5


Iterators
=========

Iterator & For-Of Operator
--------------------------

Support "iterable" protocol to allow objects to customize their
iteration behaviour. Additionally, support "iterator" protocol to produce
sequence of values (either finite or infinite). Finally, provide
convenient `of` operator to iterate over all values of an iterable object.

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

5| var fibonacci = {
5|     |next:| ((function () {
5|         var pre = 0, cur = 1;
5|         return function () {
5|             tmp = pre;
5|             pre = cur;
5|             cur += tmp;
5|             return cur;
5|         };
5|     })();
5| };
5|
5| var n;
5| for (;;) {
5|     n = fibonacci|.next()|;
5|     if (n > 1000)
5|         break;
5|     console.log(n);
5| }

Generators
==========

Generator Function, Iterator Protocol
-------------------------------------

Support for generators, a special case of Iterators containing a
generator function, where the control flow can be paused and resumed, in
order to produce sequence of values (either finite or infinite).

6| let fibonacci = {
6|     |*[Symbol.iterator]()| {
6|         let pre = 0, cur = 1;
6|         |for (;;)| {
6|             [ pre, cur ] = [ cur, pre + cur ];
6|             |yield cur|;
6|         }
6|     }
6| }
6|
6| for (|let n of fibonacci|) {
6|     if (n > 1000)
6|         break;
6|     console.log(n);
6| }

5| var fibonacci = {
5|     next: ((|function| () {
5|         var pre = 0, cur = 1;
5|         return function () {
5|             tmp = pre;
5|             pre = cur;
5|             cur += tmp;
5|             return cur;
5|         };
5|     })();
5| };
5|
5| var n;
5| for (;;) {
5|     n = fibonacci|.next()|;
5|     if (n > 1000)
5|         break;
5|     console.log(n);
5| }

Generator Function, Direct Use
------------------------------

Support for generator functions, a special variant of functions where
the control flow can be paused and resumed, in order to produce sequence
of values (either finite or infinite).

6| |function*| range (start, end, step) {
6|     while (start < end) {
6|         |yield| start;
6|         start += step;
6|     }
6| }
6|
6| for (let i |of| range(0, 10, 2)) {
6|     console.log(i); // 0, 2, 4, 6, 8
6| }

5| |function| range (start, end, step) {
5|     |var list = []|;
5|     while (start < end) {
5|         |list.push(start)|;
5|         start += step;
5|     }
5|     |return list|;
5| }
5|
5| |var r = range(0, 10, 2)|;
5| for (|var i = 0; i < r.length; i++|) {
5|     console.log(|r[i]|); // 0, 2, 4, 6, 8
5| }

Generator Matching
------------------

Support for generator functions, i.e., functions where the control flow
can be paused and resumed, in order to produce and spread sequence of
values (either finite or infinite).

6| let fibonacci = |function*| (numbers) {
6|     let pre = 0, cur = 1;
6|     while (numbers-- > 0) {
6|         [ pre, cur ] = [ cur, pre + cur ];
6|         |yield cur|;
6|     }
6| };
6|
6| for (|let n of fibonacci(1000)|)
6|     console.log(n);
6|
6| let numbers = [ |...fibonacci(1000)| ];
6|
6| let [ n1, n2, n3, ...others ] = |fibonacci(1000)|;

5| //  no equivalent in ES5


Generator Control-Flow
----------------------

Support for generators, a special case of Iterators where the control
flow can be paused and resumed, in order to support asynchronous
programming in the style of "co-routines" in combination with Promises (see below).
[Notice: the generic `async` function usually is provided by a reusable library
and given here just for better understanding. See
[co](https://github.com/tj/co) or
[Bluebird](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisecoroutinegeneratorfunction-generatorfunction---function)'s `coroutine` in practice.]

6| //  generic asynchronous control-flow driver
6| function |async| (proc, ...params) {
6|     var |iterator| = |proc(...params)|;
6|     return new Promise((resolve, reject) => {
6|         let loop = (value) => {
6|             let result;
6|             try {
6|                 result = |iterator.next|(value);
6|             }
6|             catch (err) {
6|                 reject(err);
6|             }
6|             if (result.done)
6|                 resolve(result.value);
6|             else if (   typeof result.value      === "object"
6|                      && typeof result.value.then === "function")
6|                 result.value.then((value) => {
6|                     loop(value);
6|                 }, (err) => {
6|                     reject(err);
6|                 });
6|             else
6|                 loop(result.value);
6|         }
6|         loop();
6|     });
6| }
6|
6| //  application-specific asynchronous builder
6| function makeAsync (text, after) {
6|     return new Promise((resolve, reject) => {
6|         setTimeout(() => resolve(text), after);
6|     });
6| }
6|
6| //  application-specific asynchronous procedure
6| |async|(|function*| (greeting) {
6|     let foo = |yield| makeAsync("foo", 300)
6|     let bar = |yield| makeAsync("bar", 200)
6|     let baz = |yield| makeAsync("baz", 100)
6|     return `${greeting} ${foo} ${bar} ${baz}`
6| }, "Hello").then((msg) => {
6|     console.log("RESULT:", msg); // "Hello foo bar baz"
6| })

5| //  no equivalent in ES5


# Comprehensions
# ==============
#
# List Comprehension
# ------------------
#
# Concise FP-style way to create a list/array, based on another list where
# each entry is the result of some operation.
#
# 6| let results = [
# 6|     |for (c |of| customers)|
# 6|         if (c.city === "Munich")
# 6|             { name: c.name, age: c.age } ];
#
# 5| var results = |[]|;
# 5| for (var c |in| customers)
# 5|     if (customers.hasOwnProperty(c))
# 5|         if (c.city === "Munich")
# 5|             |result.push|({ name: c.name, age: c.age });
#
# Generator Comprehension
# -----------------------
#
# Concise FP-style way to create an object, based on another object where
# each entry is the result of some operation.
#
# 6| let results = (
# 6|     for (|c of customers|)
# 6|         if (c.city == "Munich")
# 6|             { name: c.name, age: c.age } );
#
# # 5| var results = |[]|;
# 5| for (|var c = customers.next(); !c.done; c = customers.next()|)
# 5|     if (c.city === "Munich")
# 5|         |result.push|({ name: c.name, age: c.age });


Map/Set & WeakMap/WeakSet
=========================

Set Data-Structure
------------------

Cleaner data-structure for common algorithms based on sets.

6| let s = |new Set()|;
6| s.|add("hello")|.|add("goodbye")|.|add("hello")|;
6| s.|size| === 2;
6| s.|has("hello")| === true;
6| for (let key |of| s.|values()|) // insertion order
6|     console.log(key);

5| var s = |{}|;
5| |s["hello"] = true|; |s["goodbye"] = true|; |s["hello"] = true|;
5| |Object.keys(s).length| === 2;
5| |s["hello"]| === true;
5| for (var key |in| s) // arbitrary order
5|     if (s.hasOwnProperty(key))
5|         console.log(s[key]);

Map Data-Structure
------------------

Cleaner data-structure for common algorithms based on maps.

6| let m = |new Map()|;
6| m.|set("hello", 42)|;
6| m.|set(s, 34)|;
6| m.|get(s)| === 34;
6| m.|size| === 2;
6| for (let |[ key, val ] of m.entries()|)
6|     console.log(key + " = " + val);

5| var m = |{}|;
5| |m["hello"] = 42|;
5| // no equivalent in ES5
5| // no equivalent in ES5
5| |Object.keys(m).length| === 2;
5| for (key |in| m) {
5|     if (m.hasOwnProperty(key)) {
5|         var |val = m[key]|;
5|         console.log(key + " = " + val);
5|     }
5| }


Weak-Link Data-Structures
-------------------------

Memory-leak-free Object-key’d side-by-side data-structures.

6| let isMarked     = new |WeakSet|();
6| let attachedData = new |WeakMap|();
6|
6| export class Node {
6|     constructor (id)   { this.id = id;                  }
6|     mark        ()     { |isMarked.add|(this);            }
6|     unmark      ()     { |isMarked.delete|(this);         }
6|     marked      ()     { return |isMarked.has|(this);     }
6|     set data    (data) { |attachedData.set|(this, data);  }
6|     get data    ()     { return |attachedData.get|(this); }
6| }
6|
6| new foo = new Node("foo");
6| JSON.stringify(foo) === |'{"id":"foo"}'|;
6| foo.mark();
6| foo.data = "bar";
6| foo.data === "bar";
6| JSON.stringify(foo) === |'{"id":"foo"}'|;

5| // no equivalent in ES5


Typed Arrays
============

Typed Arrays
------------

Support for arbitrary byte-based data structures to implement network
protocols, cryptography algorithms, file format manipulations, etc.

6| //  ES6 class equivalent to the following C structure:
6| //  struct Example { unsigned long id; char username[16]; float amountDue; };
6| class Example {
6|     constructor (buffer = |new ArrayBuffer(24)|) {
6|         this.buffer = buffer;
6|     }
6|     set buffer (buffer) {
6|         this._buffer    = buffer;
6|         this._id        = |new Uint32Array (this._buffer,  0,  1)|;
6|         this._username  = |new Uint8Array  (this._buffer,  4, 16)|;
6|         this._amountDue = |new Float32Array(this._buffer, 20,  1)|;
6|     }
6|     get buffer ()     { return this._buffer;       }
6|     set id (v)        { this._id[0] = v;           }
6|     get id ()         { return this._id[0];        }
6|     set username (v)  { this._username[0] = v;     }
6|     get username ()   { return this._username[0];  }
6|     set amountDue (v) { this._amountDue[0] = v;    }
6|     get amountDue ()  { return this._amountDue[0]; }
6| }
6|
6| let example = new Example()
6| example.|id| = 7
6| example.|username| = "John Doe"
6| example.|amountDue| = 42.0

5| //  no equivalent in ES5
5| //  (only an equivalent in HTML5)


New Built-In Methods
====================

Object Property Assignment
--------------------------

New function for assigning enumerable properties of one or more source
objects onto a destination object.

6| var dst  = { quux: 0 };
6| var src1 = { foo: |1|, bar: 2 };
6| var src2 = { foo: |3|, baz: 4 };
6| Object|.assign|(dst, src1, src2);
6| dst.quux === 0;
6| dst.foo  === |3|;
6| dst.bar  === 2;
6| dst.baz  === 4;

5| var dst  = { quux: 0 };
5| var src1 = { foo: |1|, bar: 2 };
5| var src2 = { foo: |3|, baz: 4 };
5| Object|.keys(src1).forEach|(function(k) {
5|     |dst[k] = src1[k]|;
5| });
5| Object|.keys(src2).forEach|(function(e) {
5|     |dst[k] = src2[k]|;
5| });
5| dst.quux === 0;
5| dst.foo  === |3|;
5| dst.bar  === 2;
5| dst.baz  === 4;

Array Element Finding
---------------------

New function for finding an element in an array.

6| [ 1, 3, 4, 2 ].|find|(|x =>| x > 3); // 4

5| [ 1, 3, 4, 2 ].|filter|(|function (x) { return| x > 3; })|[0]|; // 4

String Repeating
----------------

New string repeating functionality.

6| " ".|repeat|(4 * depth);
6| "foo".|repeat|(3);

5| |Array|((4 * depth) + 1).|join|(" ");
5| |Array|(3 + 1).|join|("foo");

String Searching
----------------

New specific string functions to search for a sub-string.

6| "hello".|startsWith|("ello", 1); // true
6| "hello".|endsWith|("hell", 4);   // true
6| "hello".|includes|("ell");       // true
6| "hello".|includes|("ell", 1);    // true
6| "hello".|includes|("ell", 2);    // false

5| "hello".|indexOf|("ello") === 1;    // true
5| "hello".|indexOf|("hell") === (4 |- "hell".length|); // true
5| "hello".|indexOf|("ell") !== -1;    // true
5| "hello".|indexOf|("ell", 1) !== -1; // true
5| "hello".|indexOf|("ell", 2) !== -1; // false

Number Type Checking
--------------------

New functions for checking for non-numbers and finite numbers.

6| Number.|isNaN|(42) === false;
6| Number.|isNaN|(NaN) === true;
6|
6| Number.|isFinite|(Infinity) === false;
6| Number.|isFinite|(-Infinity) === false;
6| Number.|isFinite|(NaN) === false;
6| Number.|isFinite|(123) === true;

5| var |isNaN| = function (n) {
5|     return |n !== n|;
5| };
5| var |isFinite| = function (v) {
5|     return |(typeof v === "number" && !isNaN(v) && v !== Infinity && v !== -Infinity)|;
5| };
5| isNaN(42) === false;
5| isNaN(NaN) === true;
5|
5| isFinite(Infinity) === false;
5| isFinite(-Infinity) === false;
5| isFinite(NaN) === false;
5| isFinite(123) === true;

Number Safety Checking
----------------------

Checking whether an integer number is in the safe range, i.e., it is
correctly represented by JavaScript (where all numbers, including
integer numbers, are technically floating point number).

6| Number.|isSafeInteger|(42) === true;
6| Number.|isSafeInteger|(9007199254740992) === false;

5| function isSafeInteger (n) {
5|     return (
5|            typeof n === 'number'
5|         && Math.round(n) === n
5|         && -(Math.pow(2, 53) - 1) <= n
5|         && n <= (Math.pow(2, 53) - 1)
5|     );
5| }
5| |isSafeInteger|(42) === true;
5| |isSafeInteger|(9007199254740992) === false;

Number Comparison
-----------------

Availability of a standard Epsilon value for more precise comparison of
floating point numbers.

6| console.log(0.1 + 0.2 === 0.3); // false
6| console.log(Math.abs((0.1 + 0.2) - 0.3) < |Number.EPSILON|); // true

5| console.log(0.1 + 0.2 === 0.3); // false
5| console.log(Math.abs((0.1 + 0.2) - 0.3) < |2.220446049250313e-16|); // true

Number Truncation
-----------------

Truncate a floating point number to its integral part,
completely dropping the fractional part.

6| console.log(|Math.trunc|(42.7)) // 42
6| console.log(|Math.trunc|( 0.1)) // 0
6| console.log(|Math.trunc|(-0.1)) // -0

5| function mathTrunc (x) {
5|     return |(x < 0 ? Math.ceil(x) : Math.floor(x))|;
5| }
5| console.log(mathTrunc(42.7)) // 42
5| console.log(mathTrunc( 0.1)) // 0
5| console.log(mathTrunc(-0.1)) // -0

Number Sign Determination
-------------------------

Determine the sign of a number, including special cases of signed zero and non-number.

6| console.log(|Math.sign|(7))   // 1
6| console.log(|Math.sign|(0))   // 0
6| console.log(|Math.sign|(-0))  // -0
6| console.log(|Math.sign|(-7))  // -1
6| console.log(|Math.sign|(NaN)) // NaN

5| function |mathSign| (x) {
5|     return |((x === 0 || isNaN(x)) ? x : (x > 0 ? 1 : -1))|;
5| }
5| console.log(mathSign(7))   // 1
5| console.log(mathSign(0))   // 0
5| console.log(mathSign(-0))  // -0
5| console.log(mathSign(-7))  // -1
5| console.log(mathSign(NaN)) // NaN


Promises
========

Promise Usage
-------------

First class representation of a value that may be made asynchronously and be available in the future.

6| function msgAfterTimeout (msg, who, timeout) {
6|     return |new Promise|((|resolve|, reject) => {
6|         setTimeout(() => |resolve|(`${msg} Hello ${who}!`), timeout);
6|     });
6| }
6| msgAfterTimeout("", "Foo", 100).|then|((msg) =>
6|     msgAfterTimeout(msg, "Bar", 200);
6| ).|then|((msg) => {
6|     console.log(`done after 300ms:${msg}`);
6| });

5| function msgAfterTimeout (msg, who, timeout, |onDone|) {
5|     setTimeout(function () {
5|         |onDone|(msg + " Hello " + who + "!");
5|     }, timeout);
5| }
5| msgAfterTimeout("", "Foo", 100|, function| (msg) {
5|     msgAfterTimeout(msg, "Bar", 200|, function| (msg) {
5|         console.log("done after 300ms:" + msg);
5|     });
5| });

Promise Combination
-------------------

Combine one or more promises into new promises without having to take
care of ordering of the underlying asynchronous operations yourself.

6| function fetchAsync (url, timeout, onData, onError) {
6|     ...
6| }
6| let fetchPromised = (url, timeout) => {
6|     return |new Promise|((|resolve|, |reject|) => {
6|         fetchAsync(url, timeout, |resolve|, |reject|);
6|     });
6| }
6| |Promise.all|([
6|     fetchPromised("http://backend/foo.txt", 500),
6|     fetchPromised("http://backend/bar.txt", 500),
6|     fetchPromised("http://backend/baz.txt", 500)
6| ]).|then|((data) => {
6|     let [ foo, bar, baz ] = data;
6|     console.log(`success: foo=${foo} bar=${bar} baz=${baz}`);
6| }, (err) => {
6|     console.log(`error: ${err}`);
6| });

5| function fetchAsync (url, timeout, onData, onError) {
5|     ...
5| }
5| function |fetchAll| (request, onData, onError) {
5|     var result = [], results = 0;
5|     for (var i = 0; i < request.length; i++) {
5|         result[i] = null;
5|         (function (i) {
5|             fetchAsync(request[i].url, request[i].timeout, function (data) {
5|                 |result[i] = data|;
5|                 |if (++results === request.length)|
5|                     |onData(result)|;
5|             }, onError);
5|         })(i);
5|     }
5| }
5| |fetchAll|([
5|     { url: "http://backend/foo.txt", timeout: 500 },
5|     { url: "http://backend/bar.txt", timeout: 500 },
5|     { url: "http://backend/baz.txt", timeout: 500 }
5| ], function (data) {
5|     var foo = data[0], bar = data[1], baz = data[2];
5|     console.log("success: foo=" + foo + " bar=" + bar + " baz=" + baz);
5| }, function (err) {
5|     console.log("error: " + err);
5| });


Meta-Programming
================

Proxying
--------

Hooking into runtime-level object meta-operations.

6| let target = {
6|     foo: "Welcome, foo"
6| };
6| let proxy = |new Proxy|(|target|, {
6|     |get (receiver, name)| {
6|         return name in receiver ? receiver[name] : `Hello, ${name}`;
6|     }
6| });
6| proxy.foo   === "Welcome, foo";
6| proxy.|world| === "Hello, |world|";

5| // no equivalent in ES5

Reflection
----------

Make calls corresponding to the object meta-operations.

6| let obj = { a: 1 };
6| Object.defineProperty(obj, "b", { value: 2 });
6| obj[Symbol("c")] = 3;
6| |Reflect.ownKeys|(obj); // [ "a", "b", Symbol(c) ]

5| var obj = { a: 1 };
5| Object.defineProperty(obj, "b", { value: 2 });
5| // no equivalent in ES5
5| |Object.getOwnPropertyNames|(obj); // [ "a", "b" ]


{'fraction_non_alphanumeric': 0.22110982658959538, 'fraction_numerical': 0.042358381502890174, 'mean_word_length': 2.482186707459442, 'pattern_counts': {'":': 4, '<': 45, '<?xml version=': 0, '>': 103, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3975588', 'n_tokens_mistral': 32815, 'n_tokens_neox': 31297, 'n_words': 9740}