Proxy - object wraps another object and intercepts operations, like reading/writing properties Proxy provide a way to tweak the behavior of the existing objects at the lowest level Proxy is more powerful than just a fn wrapper, as it forwards everything to the target object Syntax let proxy = new Proxy(target, handler) target - object to wrap, can be anything, including functions handler – proxy configuration: an object with “traps”, methods that intercept operations Handler methods Triggers when get - reading a property set - writing to a property has - in operator deleteProperty - delete operator apply - function call construct - new operator getPrototypeOf - Object.getPrototypeOf setPrototypeOf - Object.setPrototypeOf isExtensible - Object.isExtensible preventExtensions - Object.preventExtensions defineProperty - Object.defineProperty , Object.defineProperties getOwnPropertyDescriptor - Object.getOwnPropertyDescriptor , for...in , Object.keys/values/entries ownKeys - Object.getOwnPropertyNames , Object.getOwnPropertySymbols , for...in , Object.keys/values/entries No own properties With an empty handler it transparently forwards operations to target
let target = {}
let proxy = new Proxy(target, {}) // empty handler
proxy.test = 5
target.test // 5
proxy.test // 5
for(let key in proxy) alert(key); // test
Get trap Get trap triggers when a property is read
let numbers = [0, 1, 2]
numbers = new Proxy(numbers, {
get(target, prop) {
if (prop in target) return target[prop]
return 0
}
})
numbers[1] // 1
numbers[123] // 0 (no such item)
let dictionary = {
'Hello': 'Hola',
'Bye': 'Adiós'
}
dictionary = new Proxy(dictionary, {
get(target, phrase) {
if (phrase in target) return target[phrase]
return "no translation"
}
}
)
dictionary['Hello'] // Hola
dictionary['Welcome to Proxy'] // no translation
Set trap Set trap triggers when a property is written
let numbers = []
numbers = new Proxy(numbers, {
set(target, prop, val) { // to intercept property writing
if (typeof val !== 'number') return false
target[prop] = val;
return true
}
})
numbers.push(1) // added successfully
numbers.push(2) // added successfully
numbers.push("test") // TypeError ('set' on proxy returned false)
numbers // Proxy {0: 1, 1: 2}
ownKeys Skip props with keys starting from "_"
let user = {
name: "John",
age: 30,
_password: "***"
}
user = new Proxy(user, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'));
}
})
for(let key in user) alert(key); // name, age
Object.keys(user) // ['name', 'age']
Object.values(user) // ['John', 30]
apply Make a wrapper function, which delays original function by 3s
function delay(f, ms) {
return new Proxy(f, {
apply(target, thisArg, args) {
setTimeout(() => target.apply(thisArg, args), ms)
}
})
}
function sayHi(user) {
alert(`Hello, ${user}!`)
}
sayHi = delay(sayHi, 3000)
sayHi.length // 1 // proxy forwards "get length" operation to the target
sayHi("John"); // Hello, John! (after 3 seconds)