When Proxy takes control
I think a lot of
Criss Jami, Killosophypsychopathscode horrors are justgeniusesprogrammers who drove so fast that they lost control
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.