Overview of ES6 Javascript 2015

Sample source code files available here on github
https://github.com/johnlee/es6

 

ECMAScript 2015 / ES6

Updated version from ES5 (2005 version). Released 2015. Supported by almost all major browsers. Some of the new features are:

  • Syntax
  • Modules and Classes
  • Types and Object Extensions
  • Iterators, Generators and Promises
  • Arrays and Collections
  • Reflect API
  • Proxy API

To see the the compatibility chart see the reference section below.If a browser doesn’t support ES6 feature, we can use different compilers/polyfils (for example to support IE11). Some Android and iOS browsers are also lacking in ES6 support so compilers/polyfils would be need for those as well.  The chart also shows server-side support like Node.

 

Syntax

There are 3 new syntax features in ES6: let, const, Block scoping.

When using the ‘var‘ keyword, JS would hoist it. So for example the following:

console.log(productId); // undefined
var productId = 12;

With the new ‘let‘ keyword the hoisting is now removed.

console.log(productId); // ERROR - productId is not defined
let productId = 12;

By using let we are ensuring that the variable is declared before being used.

Another new syntax is block scoping. For example:

let productId = 12;
{
    letproductId = 99;
}
console.log(productId); // 12
...
let productId = 12;
for (let productId = 1; productId < 5; productId++)
{}
console.log(productId);

The examples above show how block scoping now contains variables within their own blocks.

 

Block Scope

The following example shows how blocking and using the var keyword can lead to unexpected bugs. Below when using the var, the updateFunction array contains 3 functions where each returns the value 3.

let updateFunction = [];
for (var i = 0; i < 3; i++) {
   updateFunction.push(function() { returni; }); // pushes value of i, in closure
}
console.log(updateFunction[0]()); // 3
console.log(updateFunction[1]()); // 3
console.log(updateFunction[2]()); // 3

But by changing the above for loop to use a let instead, the updateFunction now contains 3 functions are each return the unique value of i.

let updateFunction = [];
for (let i = 0; i < 3; i++) {
   updateFunction.push(function() { returni; }); // pushes value of i, in closure
}
console.log(updateFunction[0]()); // 0
console.log(updateFunction[1]()); // 1
console.log(updateFunction[2]()); // 2

Another new keyword is the const. When it is used it must be initialized and cannot change. The following examples show.

const CONSTVAR1 = 100;
// CONSTVAR1 = 200; // ERROR assignement to constant
// const CONSTVAR2; // ERROR missing initailizer to constant
const CONSTVAR3 = 100;
if (1 == 1) {
   constCONSTVAR3=200; // ignored block scoped
}
console.log(CONSTVAR3); // 100

 

Arrow Functions

The ‘=>’ arrow function is ES6 way of doing lambda expressions. This means it is a first class function that can pass arguments or other functions.

var getPrice = () => 5.99;
function getPriceF() {
  return 5.99;
}
console.log(getPrice());  // 5.99
console.log(getPriceF()); // 5.99

When we have a single input to the function, we dont need parenthesis. For 2 or more params we need them.

var getPrice2 = count => count * 2;
console.log(getPrice2(2)); // 4
var getPrice3 = (count, tax) => count * 2 + tax;
console.log(getPrice3(2, .1)); // 4.1
The main purpose of having arrow functions was to help alleviate the problem with using the ‘this‘ keyword, which refers to the caller’s instance. The ‘this’ keyword is bound to the block that it is called in, which means when we have another block (or function) within a function, it cannot reference it’s parent’s instance using the ‘this’ keyword – instead it would be referencing the instance of that inner block.
class Logger {
  dumpData(data) {
    var _this = this;

    // this dumps data to a file and get the name of the file via a callback
    dump(data, function (outputFile) {
      _this.latestLog = outputFile;
    });
  }
}
// using arrow functions
class Logger {
  dumpData(data) {
    dump(data, outputFile => this.latestLog = outputFile);
  }
}

 

Default Function Parameters

Like other programming languages, ES6 now supports default function parameters.

var getTotal = function(price, total = price * .1) {
   console.log(price+total);
}
getTotal(100); // 110
getTotal(100, 200); // 300

Note that when using default parameters, it does not get shown in the argument keyword.

var getTotal2 = function(arg1, arg2 = 'hello') {
  console.log(arguments.length); // 1
}
getTotal2(1); // 1

 

Rest and Spread

Also like other programming languages, ES6 now supports function parameter rest and spread. Examples below.

var myfunc = function (arg1, ...arg2) {
  console.log(arg2);
}
myfunc('1','2','3','4'); // [2, 3, 4]

var args = [5,6,7,8,9];
var myfunc2 = function (...arg2) {
  console.log(arg2);
}
myfunc2(args); // [5,6,7,8,9]

Note that the spread operator automatically breaks apart strings to the individual characters (also automatically type casting them if necessary).

var myfunc3 = Math.max(..."23456");
console.log(myfunc3); // 6

 

Object Literal Expressions

Another new feature in ES6 is the ability to use literal expressions to create property field names dynamically. For example below, the property name is being set by a variable, as well as the value for that property.

var propName = 'PropertyName';
var propValue = 'PropertyValue';
var propObject = {
  [propName] :propValue
};
console.log(propObject); // { PropertyName: "PropertyValue" }

These also work with getters and setters as shown below.

var pName = 'PropertyName';
var pObject = {
  get [pName]() { return'PValue'; },
  set [pName](value) { }
};
console.log(pObject.PropertyName); // PValue

 

for … of loops

Also new in ES6 is the new for – of loop.

var arr = ['a', 'b', 'c'];
  for (var a of arr) {
  console.log(a); // a b c
}

 

Octal and Binary Literals

ES6 has fixed the handling of octals and binary literals. See examples below.

var value = 0b10;
var value2 = 0B10;
console.log(value); // 2
console.log(value2); // 2

 

String Interpolation

We can use interpolation to use values of variables. This is done using an ` character for the string and the ${ } for the variable to interpolate.

console.log (`hello world ${value}`);

 

Tag Template Literal with Interpolation

Another new feature of ES6 is to use tag template literal, which essentially tags strings to a function. We can use this with interpolation to create templates for strings. For example.

function myfunct(segments, ...values) {
  console.log(segments);
  console.log(values);
}
let val1 = '100';
let val2 = '200';
myfunct `Template string for showing val1 ${val1} and val2 ${val2}`;
// ["Template string for showing val1", " and val2 ", ""]
// ["100", "200"]

 

Destructuring

In ES6 we can destructure arrays into variables. Something we see in other programming languages.

let arr1 = ['100', '200', '300'];
let [a , ...b] = arr1;
console.log(a); // 100
console.log(b); // [200, 300]

We can also destructure objects. We just need to make sure that the property names match up.

let obj = {
  p1:'100',
  p2:'200',
  p3:'300'
};
let { a1, a2, a3 } = obj; // wont work as property name not match
let { p1: b1, p2: b2, p3: b3 } = obj; // remaps props
let { p1, p2, p3 } = obj;
console.log(`${a1} ${a2} ${a3}`); // undef undef undef
console.log(`${b1} ${b2} ${b3}`); // 100 200 300
console.log(`${p1} ${p2} ${p3}`); // 100 200 300

 

Modules and Classses

ES6 supports modules. They are imported when used and the module requires export statements for those properties it wants to expose publicly. Note that the ‘import’ command may not be supported by all browsers. Most browsers began supporting it in 2017. Below are some examples of import / export statements. Note that in the main HTML file, we set the modules.js file as a ‘module’ type. This is required by some browsers (Firefox).

<html>
<head>
  <meta charset="utf-8" />
  <script type="module" src="modules.js"></script>
</head>
<body>
...
// module.js
console.log('module.js'); // This file gets hoisted whenever imported, which will make this line print
export let myvar1 = 99;
export let myvar2 = 'hello';
let myvar3 = 'there';
export default myvar3;
// modules.js
import { myvar1, myvar2 as thisvar1 } from 'module.js'; // using an alias
import thisvar2 from 'module.js'; // gets the default export
console.log('Start of modules.js');
console.log(myvar1 + ' ' + thisvar1); // 99 hello
console.log(thisvar2); // there

 

Classes

In ES6 we have a new concept of classes which can be instantiated to objects. This is very similar to other programming languages. A class can have a constructor (comes with default constructor if not one define) as well as other functions. The defualt constructor does not have parameters, but a new constructor can be written that takes parameters.

The functions in a class are defined at the class’s prototype level as well and can be accessed through it. A class cannot have individual properties in the body the class. Also – note that class definitions are not hoisted, therefore they need to be declared near the top of the js file.  Examples below.

// Classes
class Myclass1 {
  //let prop1 = 99; // Syntax error
  constructor() {
    console.log('The Constructor Here');
  }
  func1() {
    console.log('99');
  }
}

let myclass = new Myclass1(); // The Constructor Here
console.log(typeof Myclass1); // function
console.log(myclass instanceof Myclass1); // true
console.log(myclass.func1 === Myclass1.prototype.func1); // true

 

Like other object oriented programming languages, ES6 also supports inheritance. There are two new keywords available – extend and super. Extend refers to child objects whereas super refers to the parent objects. Examples below.

class Myclass2 {
    constructor(name) {
    console.log('in myclass2 with '+name);
  }
  myfunc1() {
    console.log('in myfunc1');
  }
}

let x1 = new Myclass2('myclass2'); // in myclass2 with myclass2
class Myclass2b extends Myclass2 {
  constructor() {
    super('in myclass2'); // an extending class must call super if defining it's own constructor
    super.myfunc1(); // in myfunc1
    console.log('in myclass2b');
  }
}

let x2 = new Myclass2b(); // output:
// in myclass2 with in myclass2
// in myfunc1
// in myclass2b

 

Classes can also have static methods but no static properties. Properties can be added after the object has been instantiated.

class Myclass3 {
  // static prop1 = 99; // ERROR cannot have static properties
  staticfunc1() {
    return'func1';
  }
}
console.log(Myclass3.func1()); // func1
let x3 = new Myclass3();

// Static Methods are tied to the constructor only
// console.log(x3.func1()); // ERROR Object doesnt support property or method; can only be accessed through constructor
x3.prop1 = 'prop1'; // create prop outside of class
console.log(x3.prop1); // prop1

new.target

This is a new feature in ES6 to help with class inheritance. It is mostly used in constructors. ‘new.target’ always points to the constructor that was being instantiated. So when a child object calls super, new.target would point to the original child object. Examples below.

class Myclass4 {
  constructor() {
    console.log(typeofnew.target); // new.target is a function
    console.log(new.target);
  }
}
class Myclass4b extends Myclass4 {
}
var x4 = new Myclass4(); // output:
// function
// class Myclass4 { constructor() { .... }}

var x4b = new Myclass4b(); // output:
// function
// class Myclass4b extends Myclass4 { ... }

 

New Types and Object Extensions

Symbols

Symbols are new to ES6. These are mostly used for unique strings – a unique immutable data type and may be used as an identifier for object properties. It provides a way of setting unique identifiers for objects (when multiple instances of objects created). This is useful for keeping track of multiple objects. In particular, they are useful when used as a property for part of class or object. Examples below.

let sym1 = Symbol('event');
console.log(typeof sym1); // symbol
console.log(sym1.toString()); // Symbol(event)

let sym2 = Symbol('event');
console.log(sym1 === sym2); // false

let sym3 = Symbol.for('event');
let key1 = Symbol.keyFor(sym3);
console.log(key1); // event

let myobj = {
  title:'mytitle',
  [Symbol.for('prop1')]:'prop1value'// an expression used for property
}
let myval1 = myobj[Symbol.for('prop1')];
console.log(myval1); // prop1value
console.log(Object.getOwnPropertyNames(myobj));

 

Symbols can also be used for meta-programming. This is where we can manipulate the javascript engine to recognize certain objects or set different properties.

// Well Known Symbols
let myobj2 = function () { };
myobj2.prototype[Symbol.toStringTag] = 'myobj2';
let myval2 = new myobj2();
console.log(myval2.toString()); // [object myobj2] - sets a name/tag for the object

let myobj3 = [1, 2, 3];
myobj3[Symbol.isConcatSpreadable] = false;
console.log([].concat(myobj3)); // [[1,2,3]] - instead of insertin each element of myobj3, it added to whole array as single element

myobj3[Symbol.toPrimitive] = function(hint) {
  console.log(hint);
  return42;
}
let myval4 = myobj3 + 100;
console.log(myval4); // 142

 

Extensions

There are new object extensions in ES6. These apply for the following:

  • Object
  • String
  • Number
  • Math
  • RegExp
  • Function

Examples of some of these extensions are shown below.

'use strict';

console.log('Extensions');

// Object Extensions
let obj1 = {
    prop1: 1
};
let obj2 = {
    prop2: 2
};
Object.setPrototypeOf(obj1, obj2); // sets obj1 to be a type of obj2
console.log(obj1.prop2); // 2

let obj3 = {};
Object.assign(obj3, obj1, obj2); // sets obj3 to be a type of combining obj1 and obj2
console.log(obj3); // [ prop1: 1, prop2: 2 ]

let obj4 = NaN;
console.log(obj4 === obj4); // false
console.log(Object.is(obj4, obj4)); // true

// String Extensions
let str1 = 'hello world';
console.log(str1.startsWith('he')); // true
console.log(str1.endsWith('ld')); // true
console.log(str1.includes('llo')); // true

let str2 = "hello \u{1f3c4} world"; // emoji character
console.log(str2); // hello 🏄 world
console.log(str2.length); // the emoji gets counted as 2 characters
console.log(String.fromCodePoint(0x1f3c4)); // prints the emoji only

// Number Extensions
console.log(Number.parseInt == parseInt); // true - ES6 is getting rid of globals like parseInt, now call it from Number
console.log(Number.parseFloat == parseFloat); // true - ES6 encourage calling parseFloat from Number

let myval5 = 'NaN';
console.log(isNaN(myval5)); // true - ES6 moving away from these globals
console.log(Number.isNaN(myval5)); // false - its a string, Number is more accurate

let myval6 = '100';
console.log(isFinite(myval6)); // true
console.log(Number.isFinite(myval6)); // false

let myval7 = 7;
console.log(Number.isInteger(NaN)); // false
console.log(Number.isInteger(Infinity)); // false
console.log(Number.isInteger(undefined)); // false
console.log(Number.isInteger(7)); // true

// Math Extensions
console.log(Math.cos(1));
console.log(Math.cosh(1));
console.log(Math.sinh(1));
console.log(Math.tanh(1));
console.log(Math.cbrt(1)); // cube root
console.log(Math.log2(1)); // log base 2

// Regex Extensions
let pattern1 = /hello/;
console.log(pattern1.test('hello world')); // true
console.log(pattern1.test('good bye')); // false

let pattern2 = /^.ello/;
console.log(pattern2.test('hello world')); // true
console.log(pattern2.test('world hello')); // false

// Function Extensions
let myfunc1 = function calc() {
    return 0;
};
console.log(myfunc1.name); // calc

let myfunc2 = function () {
    return 0;
};
console.log(myfunc2.name); // myfunc2 - new to ES6, uses the variable name

 

Iterators, Generators and Promises

Iterator is an object that lets us go through an array or string. Generators are special functions. Generator can also do yielding, which allows functions to run continuously over time. Like other programming languages, ES6 now supports throw and returns on iterators and generators. Lastly, ES6 has promises built into it’s core.

Iterators

Iterators are new to ES6. It is an object that contains information about a collection, which can be arrays or even strings. It has various methods for traversal and information about the collection. An iterator is defined using a Symbol. Example below.

// Iterators
let array = [1,2,3];
let iterator = array[Symbol.iterator]();

iterator.next();
iterator.next();
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: false }

Note that in the example above, the iterator is not set to done until the end of the array (after the last element). The iterator methods can be overwritten to create custom functionality.

// Custom Iterator
let collection = {
    [Symbol.iterator]() {
        let thisvalue = 10;
        return {
            next() {
                let value = thisvalue > 15 ? undefined : thisvalue++;
                let done = !thisvalue;
                return { value, done };
            }
        }
    }
}
for (let c of collection) {
    console.log(c);
    if (c == undefined) { break; } // some browsers may not break out when for-loop hits done
}

 

Generators

Generators are special functions that can be yielded and may not always be on the memory stack. It is denoted by an “*” asterisk to denote it. When a generator is running, it is actually an iterator. The yields inside the generator forces the iterator to pause during execution. Example below

function *generator() {
    yield 5;
    yield 10;
}
let iterator = generator();
console.log(iterator.next()); // { value: 5, done: false }

Generally, generators contain some functionality and can be used to execute something indefinitely or over a long period of time. As the word implies, for each iteration the generator ‘generates’ the value for that iteration. This can be powerful when trying to do some I/O or where each iterative value is generated dynamically.

function *generator2() {
    let thisvalue = 1;
    while(true) {
        yield(thisvalue++);
    }
}
for (let g of generator2()) {
    if (g > 5) { break; }
    console.log(g); // 1 2 3 4 5 each being generated during the iterative steps
}

Note that generators are driven by yields and will not execute without it defined. Yields can be defined in the generator or passed in. Also, yields can be converted (or called iterator delegation) into an iterator to allow a nested generator to act as that yield. Examples below.

function *generator3() {
    let result = yield;
    console.log(result);
}
let iterator3 = generator3();
iterator3.next(); // nothing logged since no yield given
iterator3.next(100); // 100

function *generator4() {
    let params = [yield, yield, yield];
    console.log(params[2]);
}
let iterator4 = generator4();
iterator4.next(); // nothing since no yield given
iterator4.next(2); // nothing since not all yield given
iterator4.next(4); // nothing since not all yield given
iterator4.next(6); // 6

// Yield Iterator Delegation
function *generator5() {
    yield 1;
    yield* [2,3,4];
}
let iterator5 = generator5();
console.log(iterator5.next().value); // 1
console.log(iterator5.next().value); // 2
console.log(iterator5.next().value); // 3
console.log(iterator5.next().value); // 4
console.log(iterator5.next().value); // undefined

Generators can also use try-catch blocks to handle exceptions. Iterators allow error throwing to raise the errors. Examples below.

// Throw and Returns
function *generator6() {
    yield 1;
    yield 2;
    yield 3;
}
let iterator6 = generator6();
console.log(iterator6.next().value); // 1
//console.log(iterator6.throw('error')); // Error - Uncaught Error
console.log(iterator6.next().value); // never gets here

function *generator7() {
    try {
        yield 1;
        yield 2;
        yield 3;    
    } catch (e) {
        console.log('caught exception ' + e);
    }
}
let iterator7 = generator7();
console.log(iterator7.next().value); // 1
console.log(iterator7.throw('error')); // caught exception error
console.log(iterator7.next().value); // { value: undefined, done: true }

 

Promises

ES6 now supports promises natively. This use to be done using libraries like jQuery. Promises provide async execution. A promise object takes a function in it’s constructor. This function defines what needs to happen in the promise for the async execution. In the example below, the promise sets a wait time before returning.

function doasync() {
    let p = new Promise(function (resolve, reject) {
        console.log('Promise Executing');
        setTimeout(function() {
            console.log('Done!');
            resolve();
        }, 2000); // waits 2s
    });
}
let promise = doasync(); // Promise Executing ..(wait 2s).. Done!

A promise requires a resolve or reject function definition. When the promise is called, we chain a ‘then’ command to determine what needs to be done when the promise either resolves or rejects. Example below.

function doasync2() {
    return new Promise(function (resolve, reject) {
        console.log('Promise Executing');
        setTimeout(function() {
            console.log('Rejected!');
            reject();
        }, 2000); // waits 2s
    });
}

doasync2().then(function() {
    console.log('Success'); // not called since failed
}, function() {
    console.log('Fail'); // Promise Executing .. Rejected! Fail
});

In the example above, the Promise just calls ‘reject()’ without a parameter. But we can include a parameter, which would be bubbled up to the caller when fail. So in the ‘then’ function the parameter would be received. Note that we can chain multiple ‘then’ statements. This can be useful when we need to have multiple async calls running in serial.

// Multiple async calls
function doasync3() {
    return new Promise(function (resolve, reject) {
        console.log('P3 Executing');
        setTimeout(function() {
            console.log('P3 Resolved!');
            resolve(doasync2()); // calls doasync2 after doasync3 completes
        }, 2000); // waits 2s
    });
}
doasync3().then(function() {
    console.log('ASYNC3 Success'); // not printed as failed
}, function() {
    console.log('ASYNC3 Fail'); // output below
});
// P3 Executing
// P3 Resolved!
// P2 Executing
// P2 Rejected!
// ASYNC3 Fail

Another way to call multiple promises is using the ‘all’ function. Sometimes we may not want to have all promises execute based on time. There is a promise.race function that takes multiple promises and only considers the one that completes first (returns only the promise that completes first).

Promise.all([doasync, doasync2]).then(
    function() { console.log('ALL Success'); },
    function() { console.log('ALL Fail'); }
);
Promise.race([doasync, doasync2]).then(
    function() { console.log('RACE Success'); },
    function() { console.log('RACE Fail'); }
);

 

Arrays and Collections

In ES6 the array has be extended in many ways. This includes updates to the array.prototype. ES6 now supports array typing and buffering. Endianness support has been included as well, which considers whether bits are stored from big-endian (most significant bits first) or little-endian (least significant bits first). Finally, there are new collection types added including maps, sets and subclassing.

let a1 = [1,2,3];
let a2 = Array.from(a1, x => x + 100);
console.log(a2); // [ 101, 102, 103 ]

let a3 = Array.from(a1, function(x) {
    return x + this.amt;
}, { amt: 10 });
console.log(a3); // [ 11, 12, 13 ]

let a4 = [1,2,3];
a4.fill(100);
console.log(a4); // [100,100,100]

let a5 = [1,2,3];
a5.fill(100, 1);
console.log(a5); // [1,100,100]

let a6 = [1,2,3];
let b6 = a6.find(x => x > 1);
console.log(b6); // 2 (the first match only)

let i6 = a6.findIndex(function (value, index, array) {
    return value == this;
}, 2);
console.log(i6); // 1 - index where 2 found

let a7 = [1,2,3];
a7.copyWithin(0,2);
console.log(a7); // 3, 2, 3 - copys value at index 2 to index 0

console.log(...a7.entries()); // [0,3],[1,2],[2,3]
console.log(...a7.keys());  // 0 1 2
console.log(...a7.values()); // 3 2 3

 

// Buffers and Typed Arrays
let b1 = new ArrayBuffer(1024);
console.log(b1.byteLength); // 1024

// Clamped Array = values below or above the range will get clamped to the min or max values
let ia1 = new Uint8Array(b1);
ia1[0] = 0xff; // hexadecimal for 255
console.log(ia1[0]); // 255

let b2 = new ArrayBuffer(1024);
let ca = new Uint8ClampedArray(b2);
ca[0] = -100;
console.log(ca[0]); // 0 - clamps -100 to min value of 0

DataViews are available in ES6 to manage array buffers. Dataviews contain methods for manipulating the array.

Bit Endianness

Big-endian means most significant bits first. So when looking at a binary number, ex 11001010, the first bit is the highest value – so in base10 this would be 128. In little-endian, the same binary number 11001010 has the least significant bit first meaning in base10 it would be 1.

 

Map and Set

Map and Set are new features in ES6 allowing use of a collection-like data type. It is useful when dealing with arrays of objects. There are many methods supported under map. In particular, we can use map with large data sets that are iterators.  Set are collection of unique objects. When adding duplicates it will automatically ignore it. Like map, set can take an iterator as it’s declaration. Set has its own methods for reading and manipulating the collection. Examples below.

let o1 = { name: 'john' };
let o2 = { name: 'jane' };
let o3 = new Map();
o3.set(o1, 'a');
o3.set(o2, 'b');
console.log(o3.get(o1)); // a
console.log(o3.size); // 2

let o4 = new Map();
o4.set(o1, 'a');
o4.set(o2, 'b');
o4.delete(o1);
console.log(o4.size); // 1
console.log(o4.entries()); // a map iterator object is return

let s1 = new Set();
s1.add(o1);
s1.add(o2);
s1.add(o2); // duplicate ignored
console.log(s1.size); // 2

let s2 = new Set(['a','b','c']);
console.log(...s2.keys()); // a b c - set dont have keys, just returns values
console.log(...s2.values()); // a b c
console.log(...s2.entries()); // [a,a] [b,b] [c,c]

let s3 = new WeakSet([o1,o2]);
console.log(s3.size); // undefined
console.log(s3.has(o1)); // true

ES6 also supports subclassing to extend the Array object. This is useful when needing custom functionality to be added on top of the base class. Example of subclassing below.

// Subclassing
class c1 extends Array {}
let a1 = c1.from([1,2,3]);
console.log(a1 instanceof c1); // true - the from method makes it a satme type

let a2 = a1.reverse();
console.log(a2 instanceof c1); // true - reverse also preserves type

class c2 extends Array {
    sum() {
        let total = 0;
        this.map(x => total += x);
        return total;
    }
}
let a3 = c2.from([1,2,3]);
console.log(a3.sum()); // 6 

 

Reflect API

The reflect object is used to construct and manipulate objects. Reflect API is often used for domain specific languages. As shown in the next section, Reflect API is often used when working with Proxy API. It is also useful when working with inheritance and object’s prototype. Lastly – reflect lets us lock down objects so that new properties cannot be added to it.

 

console.log(typeof Reflect); // object

class r {
    constructor(a, b) {
        console.log(a + ' ' + b);
    }
}
let x = Reflect.construct(r, ['x', 'y']); // creates type r object with params
console.log(x instanceof r); // true

class r2 extends r { }
console.log(Reflect.getPrototypeOf(r2)); // class r { constructor(a,b) { .... }}

class r3 {
    constructor() {
        this.id = 9;
    }
}
let x3 = new r3();
console.log(Reflect.get(x3, 'id')); // 9
Reflect.set(x3, 'id', 99);
console.log(x3.id); // 99
console.log(Reflect.has(x3, 'id')); // true


class r4 {}
let x4 = new r4();
Reflect.defineProperty(x4, 'id', {
    value: 100,
    configurable: true,
    enumerable: true
});
console.log(x4['id']); // 100

Reflect.deleteProperty(x4, 'id');
console.log(x4['id']); // undefined


let x5 = {
    id: 200
};
x5.prop1 = 'abc';
console.log(x5.prop1); // abc

Reflect.preventExtensions(x5);
console.log(Reflect.isExtensible(x5)); // false
x5.prop2 = 'def'; // ERROR - cannot add property prop2, object is not extensible

 

Proxy API

Proxy is new to ES6 and in general a new concept in other programming languages. Basically it is a wrapper for an object or function. By wrapping, we can add more features such as for security or logging. It is like a handler that works with the target object or function. Handlers, or Proxy, can have traps – which intercepts calls to the object or function. By having these traps, we can restrict access to the object or function. Some types of traps:

  • handler.construct()
  • handler.apply()
  • handler.getPrototypeOf()
  • handler.setPrototypeOf()
  • handler.get()
  • handler.set()
  • handler.has()
  • handler.ownKeys()
  • handler.defineProperty()
  • handler.deleteProperty()
  • handler.getOwnPropertyDescriptor()
  • handler.preventExtensions()
  • handler.isExtensible()

 

console.log('Proxy API');

function e1() {
    this.name = 'john';
    this.num = '1';
}
var x1 = new e1();
var x2 = new Proxy(x1, { // x2 is a proxy for x1
    get: function(target, prop, receiver) { // using the get trap
        return 'Attempted access to ' + prop;
    }
});
console.log(x2.num); // Attempted access to num


var x3 = new Proxy(x1, { // x3 is a proxy for x1
    get: function(target, prop, receiver) {
        return Reflect.get(target, prop, receiver);
    }
});
console.log(x3.num); // 1

var x4 = new Proxy(x1, { // restrict access to parts of object
    get: function(target, prop, receiver) { // get trap
        if (prop === 'name') { // block access to the 'prop' property
            return 'Denied';
        }
        return Reflect.get(target, prop, receiver);
    }
});
console.log(x4.num); // 1
console.log(x4.name); // Denied


function f1() {
    return 99;
}
var y1 = new Proxy(f1, { // y1 is a proxy for f1 function
    apply: function(target, thisArg, argumentsList) { // apply trap
        return Reflect.apply(target, thisArg, argumentsList);
    }
});
console.log(y1()); // 99


// Proxy for Prototype
var t1 = {
    id: 1
}
var z1 = new Proxy({}, { 
    get: function(target, prop, receiver) {
        return 'property ' + prop + ' not exist';
    }
});
Object.setPrototypeOf(t1, z1); // using a Proxy to have more definitive prototype
console.log(t1.id); // 1
console.log(t1.unknownprop); // property unkonwnprop not exist

As proxy is a new concept we suspect new functionalities and use cases will arise in the future.

 

References

Compatibility Chart
https://kangax.github.io/compat-table/es6

Javascript Lambda Functions
https://www.vinta.com.br/blog/2015/javascript-lambda-and-arrow-functions/

Rapid ES6 Training
[link]