When Proxy takes control

I think a lot of psychopaths code horrors are just geniuses programmers who drove so fast that they lost control

Criss Jami, Killosophy

ES6 had introduced JavaScript Proxy, New type of object that takes control over the list of events. It opens new options that weren’t possible before. Here I’m planning to demonstrate some reasons to start wish for using it. Only “wish” since it’s ES6 feature that Babel won’t downgrade you 🙁

var proxy = new Proxy(target, handler);

The Proxy object receives three values:

  • target – the object we want to wrap
  • handler  –  functions of events we want to catch

Simple example:

In this example we wrap the object someObject, catching the event ‘set’. Once caught, we check the property changed and allow it only if it’s not ‘cantChangeIt’.

var someObject = {cantChangeIt : ':-)'}
var proxy = new Proxy(someObject, {
set: function(target, property, value, receiver) {
if(property != 'cantChangeIt'){
target[property] = value;
}
}
});

proxied.bla = 'bla';
console.log(proxied.bla);// -> bla

proxied.cantChangeIt = 'bla bla';
console.log(proxied.cantChangeIt);// -> :-)

The methods that can be cached are:

  • getPrototypeOf: traps Object.getPrototypeOf.
  • setPrototypeOf: traps Object.setPrototypeOf.
  • isExtensible: traps Object.isExtensible.
  • preventExtensions: traps Object.preventExtensions.
  • getOwnPropertyDescriptor: traps Object.getOwnPropertyDescriptor.
  • defineProperty: traps Object.defineProperty.
  • has: traps the in operator.
  • get: traps getting property.
  • set: traps setting property.
  • deleteProperty: traps the delete operator.
  • ownKeys: traps Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
  • apply: traps the function call.
  • construct: traps the new operator.

Each method has its own set of parameters, for example, the set example from above has three:

  • target – the object we wrap
  • property - the property we operate
  • value – the new value to set
  • receiver – the object which the proxy virtualizes

After writing to much, here some use cases:

Validation & value correction

Let say we want an object that holds only numeric positive value. It will convert strings into numbers, change negative to positive and remove non-numeric.

var bePositive = { }
var positiveProxy = new Proxy(bePositive, {
set: function(target, property, value, receiver) {
if(!isNaN(value)){
value = 1 * value;
if(value < 0){
value *= -1;
}
target[property] = value;
}
}
});

positiveProxy.string = "bla";
positiveProxy.positiveNumber = 123;
positiveProxy.positiveString = "123";
positiveProxy.negativeString = "-321";

console.log(positiveProxy);// -> Proxy {
// positiveNumber: 123,
// positiveString: 123,
// negativeString: 321,
// }

Revoking access to sensitive data

This time we want to hide and protect a knock knock answer:

var knock = { whosThere:'a hidden value'}
var knockKnock = new Proxy(knock, {
get: function(target, property, value, receiver) {
return property == 'whosThere'? 'who?': target[property];
},
set: function(target, property, value, receiver) {
if (property != 'whosThere'){
target[property] = value;
}
}
});

knockKnock.whosThere = 'its me';
knockKnock.whosThere == 'who?';
knock.whosThere == 'a hidden value';

prevent override existing key

var cantOverride = new Proxy({}, {
set: function(target, property, value, receiver) {
if (!target.hasOwnProperty(property)){
target[property] = value;
}
}
});
cantOverride.a = 'a';
cantOverride.a = 'b';
cantOverride.a == 'a'; // -> true

Tracing & Monitoring property accesses & go back option

var p = new Proxy({_track:[]}, {
get: function(target, property, receiver) {
if(property == 'ctrlZ'){
let last = target._track.pop();
target[last.attr] = last.value;
}
return target[property];
},
set: function(target, property, value, receiver) {
if(property != '_track'){
target._track.push({
attr:property,
value:target[property],
time:new Date()});
target[property] = value;
}
}
});
p.a = 'a';
p.a = 'b';
p.ctrlZ;
p.a == 'a';// true

And on and on it goes. Next time I’ll give an example comparing Proxy to the JavaScript class option.