Implementing Just-in-Time Object Behavioral Modifications with Proxies
Problem
You want to attach behavior to an object, such as ensuring proper data types for values
or log when an object changes, but you want to do so transparently
Solution
Use the ES 6 Proxy to attach the desired behavior to the targeted action. To ensure that
only given properties are set on an object and that a number property for an object is
given an actual number, not a string or other value that can’t be converted to a number,
use a Proxy to test the value when it’s set on the object:
function propChecker(target) { return Proxy(target, { set: function(target, property, value) { if (property == 'price') { if (typeof value != 'number') { throw new TypeError("price is not a number"); } else if (value <= 0) { throw new RangeError("price must be greater than zero"); } } else if (property != 'name') { throw new ReferenceError("property '" + property + "' not valid"); } target[property] = value; } }); } function Item() { return propChecker(this); } try { var keyboard = new Item(); keyboard.name = "apple"; // OK keyboard.type = "red delicious"; // throws ReferenceError keyboard.price = "three dollars"; // throws TypeError keyboard.price = -1.00; // throws RangeError } catch(err) { console.log(err.message); }
EXPLAIN
The Proxy object wraps an object and can be used to trap specific actions, and then
provide additional or alternative behaviors based on both the actions and the object’s
data at the time of the action. The Mozilla Developer Network documentation for the
object refers to the capability as meta-programming API.
In the solution, because we want to modify what happens when properties are set on
the target object, and the target objects are themselves instances, the proxy of the object
is returned, rather than the object directly, in the constructor. This is accomplished by
passing this as the target object for the Proxy call, and then defining a handler function,
propChecker, for the object.
In the handler, we’re only interested in altering the semantics of the set operation. The
function used to handle the operation is passed the target object, the property, and the
new property value.
In the function, the code tests to see if the property being set is
price, and if so, it then checks to see if it’s a Number. If it isn’t, a TypeError is thrown.
If it is, then the value is checked to make sure it’s greater than zero. If it’s not, then a
RangeError is thrown.
Finally, the property is checked to see if it’s name, instead. If it isn’t, the final error, a
ReferenceError, is thrown. If none of the error conditions is triggered, then the property
is assigned the value. .
Based on all of this, new instances of the target object can only have the two properties
set, and the numeric property must be a number greater than zero.
Proxy supports a considerable number of traps, which I’ve listed in Table 10-1. The table
lists the API trap, followed by the parameters the handler function expects, expected
return value, and how it’s triggered. These are all pulled directly from the Harmony
documentation, as it’s the most current listing at this time. The important point to keep
in mind is that each of these is a trap that you can trigger and redefine the semantics of.
Proxies can also wrap JavaScript built-in objects, such as Arrays, or even the Date object.
In the following code, a proxy is used to redefine the semantics of what happens when
the code accesses an array. The get passes the target, name, and receiver to the proxy
handling function. The value of the array at the given index is checked and if it’s a value
of zero (0), a value of false is returned; otherwise, a value of true is returned:
var handler = { get: function(arry, indx){ if (arry[indx] === 0) { return false; } else { return true; } } }; var arr = [1,0,6,1,1,0]; var a = new Proxy(arr, handler); console.log(a[2]); // true console.log(a[0]); // true console.log(a[1]); // false
The array value at an index of 2 is not zero, so true is returned. The same is true for the value at an index of zero. However, the value at the index of 1 is zero, so false is returned. This behavior holds anytime this array proxy is accessed
No comments:
Post a Comment