ES6 Proxies, Generators and Symbols
Proxy gives us the ability of meta programming. Proxies can be used to intercept operations on any object, this is what makes proxies a powerful feature.
A proxy is created using two arguments:
- target: this is the object itself that you want to proxy.
- handler: handler object tells the operations that will be intercepted.
Proxies will behave exactly the same as its target, all the operations on proxy if not intercepted will be forwarded to its target object.
For example, you could intercept all read and write to an object by declaring a handler.get and handler.set method respectively, these are called traps, similarly the construct and delete operation traps.
In the above example, you can see how the get operation is intercepted to log the discounted percent, as we already know that those operations which are not defined in the proxy for intercepting will be forwarded to the target object, in the above example when the transactionAmt value is set again, the operation was forward to respective transaction object.
You can find all methods/traps of a Proxy class in MDN web docs, Proxy object is supported on all major browsers.
There is one static function on Proxy object revocable() which lets you create a revocable proxy, just like new Proxy() you can perform all operations except this proxy can be revoked later and a proxy can be revoked by calling revoke() method, after revoking a proxy all method will throw a TypeError, you can find the example below.
So where can we use the Proxies?
- Return default values in getters in case of invalid data value.
- Intercepting and logging a reads/writes to objects
- Do data validation before setting object value like age validation or check if the value being set is part of cached enumerated values like gender.
- Custom error handling as part of data validation.
Symbols are primitives, a fundamental value of the language, they are immutable. A symbol value may be used as an identifier for object properties; this is the data type’s primary purpose.
Every symbol returned by Symbol() is unique and has its own identity. A Symbol is created via a factory function, i.e,
It cannot be instantiated with the new operator, doing so will result in a TypeError.
The other use case of Symbols can be to check for the uniqueness.
Although symbols are primitive types, type coercion to other primitive types results in a TypeError except for boolean and Object. Symbols can be global which can be available across the codebase and can be created using use the methods Symbol.for() and Symbol.keyFor() to set and retrieve symbols from the global symbol registry.
The predefined symbols defined by ES6 are called well-known symbols. A well-known symbol is a static property of the Symbol object. To get the array of own property symbols from an object Objects Object.getOwnPropertySymbold() static method can be used.
A Generator is a special function, A Generator function returns the Generator object which can be iterated by using the next () method or a for…of loop. It is like writing iterators which produces a sequence of results.
A generator executes function until it encounters a yield where it suspends its execution and returns an object containing the value of the encountered yield and the done status. Generator returns done status as true if it has completed execution.
Apart from being used as an iterator, a Generator function can also help us set the sequence of async function, example of which is a fetchUserDetails and subsequent validations to be done after fetching fetchUserDetails in the following example code.
Another really good feature of meta programming is Reflection, which provides us almost the same features as the proxies but the purpose of Reflection is to know the low level information of the objects, but we will discuss this as another topic.