Last active
October 13, 2016 16:30
-
-
Save androide-osorio/f9e05db8d0c0de70d04f8b1f942e657d to your computer and use it in GitHub Desktop.
ES2015 Roadtrip to awesomeness - Proxies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Proxies | |
*/ | |
/* | |
* Proxies allow to override the default behavior | |
* from an object's default operations. | |
* | |
* Proxies enable creation of objects with the full range of behaviors available to host objects. | |
* Can be used for interception, object virtualization, logging/profiling, etc. | |
*/ | |
const person = { name: 'Andrew', age: 25 }; | |
// a proxy receives 2 things: | |
// 1 -> the target object to intercept. | |
// 2 -> the "handler", where all the operations that will be | |
// intercepted or overwritten are specified | |
const personProxy = new Proxy(person, { | |
// each of the methods or properties | |
// defined here are called "traps" | |
get(target, key) { | |
console.log('someone is asking for', target, key); | |
return 'Fck you 🇩🇪'; | |
} | |
set(target, key, value) { | |
if(typeof value === 'string') { | |
target[key] = value.trim().toUpperCase(); | |
} | |
} | |
}); | |
// will output: | |
// -> someone is asking for [Object object] name | |
// -> fck you [germany flag] | |
console.log(personProxy.name); | |
// testing new setter | |
personProxy.motto = " I'm the fucking boss!!! "; | |
console.log(personProxy.motto); // -> "I'M THE FUCKING BOSS!!!" | |
// Proxies have a reference to the original object, | |
// they do not copy its values. If you modify | |
// the original object, those changes will be reflected | |
// in the proxy object and viceversa | |
const dummyObject = {}; | |
const dummyObjectProxy = new Proxy(dummyObject, { | |
get(target, key) { | |
return target[key]; | |
}, | |
set(target, key, value) { | |
target[key] = value; | |
} | |
}); | |
console.assert(dummyObject !== dummyObjectProxy); // -> true | |
// if we change the original object directly, | |
// the change will be reflected in the proxy as well | |
dummyObject.foo = true; | |
console.assert(dummyObjectProxy.foo === true); // -> passes | |
dummyObjectProxy.bar = false | |
console.assert(dummyObject.bar === false); // -> passes | |
// -------------------------------------------------- | |
// practical example | |
const phoneHandler = { | |
set(target, key, value) { | |
target[key] = value.match(/[0-9]/g).join(''); | |
}, | |
get(target, key) { | |
const phoneRegex = /(\d{3})(\d{3})(\d{4})/; | |
return target[key].replace(phoneRegex, '($1)-$2-$3') | |
} | |
}; | |
const phoneNumbers = new Proxy({}, phoneHandler); | |
phoneNumbers.work = '(555) 555-5555'; | |
console.log(phoneNumbers.work); // -> "(555)-555-5555" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----------------------------------------------------- | |
// PROXY HANDLERS | |
// the handlers that can be used in proxy are the same | |
// ones accesible using the reflection API. These hooks | |
// or traps refer to JavaScript's internal methods | |
// full list here https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Proxy | |
// Here is a Proxy where we're defining the same behaviour as the default: | |
const proxy = new Proxy({}, { | |
apply: Reflect.apply, | |
construct: Reflect.construct, | |
defineProperty: Reflect.defineProperty, | |
getOwnPropertyDescriptor: Reflect.getOwnPropertyDescriptor, | |
deleteProperty: Reflect.deleteProperty, | |
getPrototypeOf: Reflect.getPrototypeOf, | |
setPrototypeOf: Reflect.setPrototypeOf, | |
isExtensible: Reflect.isExtensible, | |
preventExtensions: Reflect.preventExtensions, | |
get: Reflect.get, | |
set: Reflect.set, | |
has: Reflect.has, | |
ownKeys: Reflect.ownKeys, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----------------------------------------------------- | |
// REVOCABLE PROXIES | |
// ----------------------------------------------------- | |
// proxies can be revoked, which means the proxy object | |
// would no longer be usable. | |
// This could be useful when you need a temporary proxy | |
// that needs to garbage collected later on | |
const revocableProxy = Proxy.revocable({ foo: 'bar' }, { | |
get(target, property) { | |
const prop = Reflect.get(this, peroperty); | |
if(typeof prop === 'string') { | |
return target[property].toUpperCase(); | |
} | |
return prop; | |
} | |
}); | |
// a revocable proxy returns an object containing | |
// the actual proxy object, and a revoke() method | |
console.log(revocableProxy); | |
console.log(revocableProxy.foo); // -> "BAR" | |
// revoke the proxy | |
revocableProxy.revoke(); | |
// once a proxy is revoked, all of its handlers | |
// will return a TypeError | |
console.log(revocableProxy.foo); // -> TypeError |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sources for this gist:
https://www.keithcirkel.co.uk/metaprogramming-in-es6-part-3-proxies/
The excellent ES6 for everyone course by Wes Bos