Categories
Modern JavaScript

Best of Modern JavaScript — Proxy and Object

Spread the love

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at metaprogramming with JavaScript proxies.

Revocable Proxies

We can create revocable proxies with ES6.

To do that, we call the Proxy.revocable method with the target and handler arguments.

For example, we can write:

const target = {};
const handler = {
  get(target, propKey, receiver) {
    return 'foo';
  },

  has(target, propKey) {
    return true;
  }
};
const {
  proxy,
  revoke
} = Proxy.revocable(target, handler);

The Proxy.revocable function takes the target object which we want to change the behavior of.

handler is an object which has various methods to let us control the behavior of target .

It returns an object with the proxy object and the revoke function to revoke the proxy.

We can call revoke to stop using the proxy.

For example, if we have:

const target = {};
const handler = {
  get(target, propKey, receiver) {
    return target[propKey];
  },

has(target, propKey) {
    return true;
  }
};
const {
  proxy,
  revoke
} = Proxy.revocable(target, handler);

proxy.foo = 'bar';
console.log(proxy.foo);

revoke();

console.log(proxy.foo);

Then the 2nd console log will give us an error since the proxy has been revoked with the revoke function.

Therefore, we get ‘Uncaught TypeError: Cannot perform ‘get’ on a proxy that has been revoked’.

Proxies as Prototypes

We can use proxies as a prototype of an object.

For example, we can write:

const target = {};
const handler = {
  get(target, propKey, receiver) {
    return target[propKey];
  },

  has(target, propKey) {
    return true;
  }
};
const proto = new Proxy(target, handler);
const obj = Object.create(proto);

to use the Object.create method to create a proxy object and use that as the prototype.

Forwarding Intercepted Operations

We can forward intercepted operations with proxies.

For example, we can write:

const target = {};
const handler = {
  deleteProperty(target, propKey) {
    return delete target[propKey];
  },
  has(target, propKey) {
    return propKey in target;
  },
}

const proto = new Proxy(target, handler);

to add the forward the delete operation and the in operation.

We just do the same thing that we expect without the proxy within the handler methods.

Wrapping an Object Affects this

Since the handler is an object, it has its own value of this .

Therefore, if we have:

const target = {
  foo() {
    console.log(this === target)
    console.log(this === proxy)
  }
};
const handler = {};
const proxy = new Proxy(target, handler);

proxy.foo()

Then the first console log is false and the 2nd is true since we called foo on proxy .

On the other hand, if we have:

const target = {
  foo() {
    console.log(this === target)
    console.log(this === proxy)
  }
};
const handler = {};
const proxy = new Proxy(target, handler);

target.foo()

Then the first console log is true and the 2nd is false .

Objects that can’t be Wrapped Transparently

If an object has some private data, then a proxy can’t wrap the object transparently.

For example, if we have:

const _name = new WeakMap();
class Animal {
  constructor(name) {
    _name.set(this, name);
  }
  get name() {
    return _name.get(this);
  }
}

Then if we have:

const mary = new Animal('mary');
const proxy = new Proxy(mary, {});
console.log(proxy.name);

We get that the name property in undefined .

This is because the name is stored in the WeakMap instead of as a direct property of this itself.

this is the proxy so it doesn’t have the name property.

Conclusion

Proxies can be revocable, and the value of this are different between the proxy and the original object.

Leave a Reply

Your email address will not be published. Required fields are marked *