Metaprogramming: An Introduction to JavaScript(ES6) Proxy
Tags: javascript, webdev, coding, metaprogramming
Published: 2019-06-09 14:31:00

The concept of *Metaprogramming* is not new. There are many programming languages like, Lisp, Scala, Clojure, Rust, Haskell, etc already got the use of it. *JavaScript* is not really behind either! 

Before we go any further, let us understand, What is Metaprogramming?

### Metaprogramming
Metaprogramming is nothing less than a *Magic*! Truly, how about writing a program to Read, Modify, Analyze and even to Generate a *Program*? Doesn't it sound Wizardry and Powerful?
![magic.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1559815386583/RGcMHgRZZ.gif)
*Image Courtesy: GIPHY*

Wikipedia defines *Metaprogramming* as,

>Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data.

So basically, it is the Program that deals with the Meta Data of another program and able to do lot of useful things.

### Meet Proxy
> Proxy wraps objects and intercepts their behavior through traps

Among several ways we can do *Metaprogramming* in JavaScript, usage of *Proxy* object is one of the important one. Proxy object is an ES6 concept used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

Here are few useful terms you need to remember and use:
- target: an *Object* which the proxy virtualizes.
- handler: a Placeholder Object which contains *traps*.
- trap: the *Methods* that provide property access of the *target* object.

It is perfectly fine, if you haven't got much from the description above. We will understand it very easily through code and examples.

# Code Time
Here is the syntax for creating a Proxy Object:
```javascript
let p = new Proxy(target, handler);
```

Now let us take an example of an `employee` object and try to print some of the properties of it:
```javascript
const employee = {
    firstName: 'Tapas',
    lastName: 'Adhikary'
};

console.group('employee');
    console.log(employee.firstName);
    console.log(employee.lastName);
    console.log(employee.org);
    console.log(employee.fullName);
console.groupEnd()
```
Well, we know the expected output would be,
```bash
employee
  Tapas
  Adhikary
  undefined
  undefined
```
Now let us use the `Proxy` object to alter this program of `employee` handling and provide some behavior to it:
- Step 1: Create a `Handler` that uses a `Trap`

We will be using a trap called `get` which is a trap for getting a property value. Here is our Handler:
```javascript
let handler = {
    get: function(target, fieldName) {        

        if(fieldName === 'fullName' ) {
            return `${target.firstName} ${target.lastName}`;
        }

        return fieldName in target ?
            target[fieldName] :
                `No such property as, '${fieldName}'!`

    }
};
```
The above `handler` helps to create the value for fullName property. It also adds a better error message in case, we are dealing with a missing property.

- Step 2: Create a `Proxy` Object

As we have the *target* as `employee` object and the *handler*, we will be able to create a `Proxy` object as:
```javascript
let p = new Proxy(employee, handler);
```

- Step 3: Access the properties on the `Proxy` object

```javascript
console.group('proxy');
    console.log(p.firstName);
    console.log(p.lastName);
    console.log(p.org);
    console.log(p.fullName);
console.groupEnd()
```
You should be seeing the output as,
```bash
proxy
  Tapas
  Adhikary
  No such property as, 'org'!
  Tapas Adhikary
```
Notice how we have *magically* changed things for the `employee` object.

In Previous example, we used  a `trap` called *get*. Here are the list of available traps:
- apply
- construct
- defineProperty
- deleteProperty
- get
- getOwnPropertyDescriptor
- getPrototypeOf
- has
- isExtensible
- ownKeys
- preventExtensions
- set
- setPrototypeOf

More on these can be found here,  [Proxy - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)

### Proxy for Validation of Values
Let's create a handler(we can name it as, validator):
```javascript
const validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if(!Number.isInteger(value)) {
                throw new TypeError('Age is always an Integer, Please Correct it!');
            }
            if(value < 0) {
                throw new TypeError('This is insane, a negative age?');
            }
        }
    }
};
```
Again, we can create a `Proxy` object as:
```javascript
let p = new Proxy(employee, validator);
```
If you do,
```javascript
p.age = 'I am testing the blunder';
```
The output would be a `TypeError` as,
```bash
TypeError: Age is always an Integer, Please Correct it!
    at Object.set (E:\Projects\KOSS\metaprogramming\js-mtprog\proxy\userSetProxy.js:28:23)
    at Object.<anonymous> (E:\Projects\KOSS\metaprogramming\js-mtprog\proxy\userSetProxy.js:40:7)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)
    at bootstrap_node.js:609:3
```
Similarly, try doing this!
```javascript
p.age = -1;
```

### Use-cases
`Proxy Object` is a very powerful concept. There are several use-cases where this concept can be used. Here are few:
- Protect *ID* field from deletion from an Object(trap: deleteProperty)
- Tracing Property Accesses(trap: get, set)
- Data Binding(trap: set)
- Revocable references
- Manipulate the `in` operator behavior

... and many many more.

### Last Note
Hope you liked the concept of `Proxy Object`. Try it out, it is Fun! Feel free to access the examples from  [My Github Repo](https://github.com/atapas/js-mtprog/tree/master/proxy).

'Proxy' is not the only concept for JavaScript based *Metaprogramming*, there are are others like, *Reflect*. That is coming soon.

This post was originally published [on my blog](https://blog.greenroots.info/) powered by HashNode.