Object-Oriented JavaScript — Metaprogramming and Proxies
By: John Au-Yeung
Tags: JavaScript, Web Development, Software Development, Technology, Programming

Photo by Aryo Kadiono on Unsplash

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

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

Metaprogramming and Proxies

Metaprogramming is a programming method where a program is aware of its structure and manipulating itself.

There’re multiple ways to do metaprogramming.

One is introspection. This is where we have read-only access to the internals of a program.

Self-modification is making structural changes to the program.

Intercession is where we change language semantics.

In JavaScript, we can do this with proxies.

They let us control how objects are accessed and set.

Proxy

We can use proxies to determine the behavior of an object.

The object being controlled is called the target.

We can define custom behaviors for basic operations on an object like property lookup, function call, and assignment.

A proxy needs 2 parameters,

One is the handler, which is an object with methods to let us change the behavior of object operations.

Target is the target that we want to change the operations to.

For instance, we can create a proxy to control an object by writing:

const handler = {

get(target, name) {

return name in target ? target[name] : 1;

}

}

const proxy = new Proxy({}, handler);

proxy.a = 100; console.log(proxy.a);

console.log(proxy.b);

We created a proxy with handler with the handler object.

The get method lets us control how properties are retrieved.

target is the object that we’re controlling.

The name is the property name we want to access.

In the get method, we check if the name proxy exists.

If it does we return the target value, otherwise we return 1.

Then we create a proxy with the Proxy constructor.

A first argument is an empty object.

handler is our handler for controlling the operations.

proxy.a is defined, so its value is returned.

Otherwise, we return the default value.

Also, we can use proxies to validate values before setting them to an object.

For instance, we can trap the set handler by writing:

const ageValidator = {

set(obj, prop, value) {

if (prop === 'age') {

if (!Number.isInteger(value)) {

throw new TypeError('age must be a number');

}

if (value < 0 || value > 130) {

throw new RangeError('invalid age range');

}

}

obj[prop] = value;

}

};

const p = new Proxy({}, ageValidator);

p.age = 100;

console.log(p.age);

p.age = 300;

We have the set method with the obj , prop , and value parameters.

obj is the object we want to control.

prop is the property key.

value is the property value we want to set.

We check if prop is 'age' so that we validate the assignment of the age property.

Then we check if it’s an integer and if it’s not we throw an error.

We also throw an error if it’s out of range.

Then we create a proxy with the Proxy constructor with the ageValidator as the handler and an empty object to control.

Then if we try to set p.age to 300, we get a RangeError .

Photo by Scott Webb on Unsplash

Conclusion

Proxies let us control how object properties are retrieved and set.