Hi i'm Craig
I do JS AT
You can find me ON the interWebz:

PROXIES ROCK
(sies)


What Do I mean by proxy?
Not a web proxy
An ES2015 Proxy object

Spec?
Support?
Wat is it?
Proxies are a meta-programming feature that allow you to change how fundamental JS operations work on a target object.
By default, they don't do much:
let target = {};
let handlers = {};
let proxy = new Proxy(target, handlers);
console.log(proxy.foo); // undefined

Meta-programming?
function createFunction (name) {
return `
(function () {
return function ${name} () {
console.log('hello ${name}!');
return createFunction('${name}');
}
})()
`;
}
eval(createFunction('Craig'))();
Programs operate on data and data structures.
Meta-programs operate on programs:

Meta-programming

Object.keys();
function barify (object, key) {
object[key] = 'bar';
}
function doMaths (a, b, c) {
return a * b + c;
}
doMaths.length; // 3
A ProxY provides a mechanism for changing JS operations at runtime!
What does that look like?
let object = {};
let handlers = {};
let proxy = new Proxy(object, {
get (target, key) {
console.log(target, key);
}
});
proxy.foo; // object, 'foo'
Here we have provided a trap for the "get" operation. Whenever a property is accessed on our object, the trap will fire.
What kind of traps are there?

get
set
has
deleteProperty
defineProperty
getOwnPropertyDescriptor
ownKeys
getPrototypeOf
setPrototypeOf
isExtensible
preventExtensions
set:
let manners = new Proxy({}, {
set (target, key, value) {
console.log(target, key, value);
if (value.match(/please$/)) {
Reflect.set(target, key, value);
} else {
console.log('nah');
}
}
});
manners.foo = 'bar'; // 'nah';
manners.foo = 'bar, please';
has:
let object = { foo: 'bar' };
let liar = new Proxy(object, {
has (target, prop) {
return !Reflect.has(target, prop);
}
});
console.log('foo' in liar); // false;
console.log('baz' in liar); // true;
Note that in these examples we've used the Reflection API. Every Proxy trap has an equivalent method on the global Reflect object (also part of ES2015)
deleteProperty:
let object = { foo: 'bar' };
let hoarder = new Proxy(object, {
deleteProperty (target, prop) {
return false;
}
});
delete hoarder.foo;
console.log(hoarder.foo); // 'bar'

Function only traps:
apply:
construct:
var func = function () { };
var trapped = new Proxy(func, {
apply () {
console.log('Hello?');
},
construct () {
console.log('What do you want?');
}
});
console.log(trapped()); // 'Hello?'
console.log(new trapped()); // 'What do you want?'
Invariants:
If the following invariants are violated, the proxy will throw a TypeError:
- A property cannot be added, if the target object is not extensible.
- A property cannot be added as or modified to be non-configurable, if it does not exists as a non-configurable own property of the target object.
- A property may not be non-configurable, if a corresponding configurable property of the target object exists.
- If a property has a corresponding target object property then Object.defineProperty(target, prop, descriptor) will not throw an exception.
- In strict mode, a false return value from the defineProperty handler will throw a TypeError exception.
A trap may have a set of invariants, which are rules around behaviour that cannot be modified.
For example, the deleteProperty trap:
WHY?

Real use cases?
and more...
My use case:
let mock = ineeda();
// [[Get]] trap:
console.log(mock.foo.bar.baz); // Proxies all the way down.
// [[Apply]] trap:
console.log(mock.foo.bar.baz()); // throws Error('Not implemented');
// [[Set]] trap:
mock.value = 42;
console.log(mock.value) // 42;
// Whole bunch of traps:
sinon.stub(mock.some.deeply.nested, 'property');
What can't they do?

Proxies are powerful, but they aren't omnipotent
Missing traps?
function Point (x, y) {
return new Proxy({ x, y }, {
add (a, b) {
return new Proxy(a.x + b.x, a.y + b.y);
}
});
}
var p1 = Point(1, 2);
var p2 = Point(3, 4);
console.log(p1 + p2); // { x: 4, y: 6 }
operator overloading:
Note: unfortunately, this isn't real JS 😢
Missing traps?
var object = {};
var falsy = new Proxy(object, {
toBoolean (target) {
return false;
}
});
console.log(!!object); // true;
console.log(!!falsy); // false;
toBoolean:
Note: unfortunately, this isn't real JS 😢
FIN


Questions?
Proxies rocksies
By Craig Spence
Proxies rocksies
ES2015 Proxies
- 3,694