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