Learning ECMAScript 6 features in Node.js console

Marco Minetti | Sep 09, 2015

nodejs console ecmascript6 javascript snippet learning babeljs
Article Goal

This article offers a list of code examples to experience and learn new ECMAScript 6 features. You will get effective code snippets that simply work out of the box in the Node.js console.

The new official specification of Javascript is the ECMAScript 6 as of the ratification in June 2015. It introduces major features, thus making Javascript a much more powerful and complete language for programming the web.

While ECMAScript 6 implementation is currently underway in major Javascript engines, as of the time of writing, Node.js console supports ECMAScript 6 and 7 through the amazing Babel.js compiler. Babel.js is still under active development and some limits applies to the features available.

To start learning and getting practiced with the new Javascript, you can follow the excellent "Learn ES2015" article at Babel.js.

  

Here follows a table with brief explanations and examples related to new or upcoming ECMAScript features (the examples are taken from Luke Hoban's excellent es6features repository with some changes to make them work).

You can easily get Node.js console through the installation tutorial at "Console: handling Javascript Node.js runtime like a pro".

  

Features Examples

Arrow Functions

compact functions (a.k.a. lambda expression in C#)

var evens = [ 2, 4, 6, 8];

// arrow functions as map handler
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
 
// arrow functions as shorthand for other functions
var print = ((a, b, c) => console.log(a, b, c));
print(evens, odds, nums);

// arrow function with multiple lines body
nums.forEach(v => {
  if (v % 5 === 0)
    console.log(v);
});

Lexical This (in Arrow Functions)

upper scope this available in arrow function (inner) body

// look at printFriends implementation 
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
};
 
bob._friends.push('Mark');
bob.printFriends();

Enhanced Object Literals

__proto__, handler, toString and computed [] property names

var handler = {};
var obj = {
    // sets internal prototype (requires native support, try uncomment following line)
    //__proto__: {},
    // does not set internal prototype
    '__proto__': 'not the prototype!',
    // shorthand for ‘handler: handler’
    handler,
    // methods
    toString() {
     // super calls
     return "d " + super.toString();
    },
    // computed (dynamic) property names
    [ "prop_" + (() => 42)() ]: 42
};
 
console.log(obj.toString());
console.log(obj.prop_42);

Classes

replacement for prototype

// class definition with constructor, super class override and static methods
class VerboseArray extends Array {
  constructor(...args) {
    console.log('instancing array');
    super(...args);
  }
  dump() {
    console.log('dumping...');
    this.forEach((item, index) => console.log(index, item));
  }
  push(...args) {
    console.log('pushing %s', ...args);
    super.push(...args);
  }
  static example() {
	var result = new VerboseArray();
	result.push('bob');
    result.push('mark');
    result.push('lisa');
    return result;
  }
}
 
var myVerboseArray = VerboseArray.example();
myVerboseArray.push('fred');
myVerboseArray.map(item => console.log(item+'?'));
myVerboseArray.dump();

Template Strings

interpolated, multiline, unescaped strings with grave accent

// 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", a = 5, b =10;
`Hello ${name}, how are you ${time}? The sum is ${a + b}`

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

Tagged Template Strings

modify output of template strings using functions as tags

var a = 5;
var b = 10;

function tag(strings, ...values) {
  console.log(strings[0]); // "Hello "
  console.log(strings[1]); // " world "
  console.log(values[0]);  // 15
  console.log(values[1]);  // 50

  return "Bazinga!";
}

// process the template string through the tag function
tag`Hello ${ a + b } world ${ a * b}`;

Destructuring

variable declaration using pattern matching and binding for arrays and objects

(function(){
 
// list matching
var [a, , b] = [1,2,3];
 
console.log(a, b);
 
function getObject() {
	return { op: 1, lhs: { op: 2 }, rhs: 3 };
}
// object matching
var { op: a, lhs: { op: b }, rhs: c } = getObject();

console.log(a, b, c);
// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getObject();
 
console.log(op, lhs, rhs);

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

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

// fail-soft destructuring with defaults
var [a = 1] = [];
console.log(a === 1);
 
// rest elements for Arrays
var [a,b,...c] = [1,2,3,4]
console.log(a,b,c);
 
})();

Evaluated Arguments

default, rest arguments arrays, spreading

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


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


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

Block-scoped Binding Constructs

let and const

// you will get an exception, that's right, check out last line!
function f() {
  {
    let x;
    {
      // okay, block scoped name
      const x = "sneaky";
      // error, const
      x = "foo";
    }
    // okay, declared with `let`
    x = "bar";
    // error, already declared in block
    let x = "inner";
  }
}

Iterators and For...Of

custom iteration implementation for objects

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

Generators and For...Of

simplified iterator implementation with function* and yield

var fibonacci = {
  [Symbol.iterator]: function*() {
    var pre = 0, cur = 1;
    for (;;) {
      var temp = pre;
      pre = cur;
      cur += temp;
      // return cur value and store its state on each execution 
      yield cur;
    }
  }
}

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

Unicode

better unicode support

// same as ES5.1
"??".length == 2

// new RegExp behaviour, opt-in ‘u’
"??".match(/./u)[0].length == 2

// new form
"\u{20BB7}" == "??" == "\uD842\uDFB7"

// new String ops
"??".codePointAt(0) == 0x20BB7

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

Efficient Data Structures

Map, Set, WeakMap, WeakSet structures

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

Symbols

new primitive type to key properties with

var MyClass = (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";
 
  return MyClass;
})();

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

Subclassable Built-ins

extendable built-in classes (Array, Date, DOM in browsers)

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

var arr = new MyArray();
arr.push(0);
arr.push(1);
arr.length == 2;

Built-ins APIs

new library additions, including core Math libraries, Array conversion helpers, and Object.assign for copying

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"

(function(){
	return Array.from(arguments);  // 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
 
for (var item of ["a", "b", "c"].entries()) console.log(item); // iterator [0, "a"], [1,"b"], [2,"c"]
for (var item of ["a", "b", "c"].keys()) console.log(item); // iterator 0, 1, 2
for (var item of ["a", "b", "c"].values()) console.log(item); // iterator "a", "b", "c"

Object.assign({}, { origin: { a: true } });

Literals

added binary and octal literals

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

Promises

asynchronous programming support

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

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

Reflect APIs

reflection API exposing the runtime-level meta-operations on objects

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

Reflect.ownKeys(O); // ['a', 'b', Symbol(c)] but you need native support

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

Tail Calls

stack overflow prevention for calls in tail-position

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)

  

Troubleshooting Node.js console

If you are experiencing problems with Node.js console module, please check its dedicated troubleshooting section.

ECMAScript 6 specific issues are reported below.

Some ECMAScript features not working

Remember that the support for some latest and, more important, experimental ECMAScript features can't be implemented without natives.

Please check on Babel.js documentation and experimental usage whether the feature is supported and whether limits applies.

  

If you are experiencing any other problem, please contact me so we can extend this section.

You can also report bugs or feature requests directly at the GitHub project page.

  

Further readings and related articles