Skip to content

Instantly share code, notes, and snippets.

@dotproto
Last active February 14, 2024 01:53
Show Gist options
  • Save dotproto/1e66288d5799b8e4b27be19bb44b9cf3 to your computer and use it in GitHub Desktop.
Save dotproto/1e66288d5799b8e4b27be19bb44b9cf3 to your computer and use it in GitHub Desktop.
Some test code generated while trying to wrap my head around how JS proxies work and how I might leverage them
// Resources
//
// - http://exploringjs.com/es6/ch_proxies.html
// - https://github.com/tvcutsem/harmony-reflect/issues/41
// - https://esdiscuss.org/topic/proxies-stratifying-tostring-valueof
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive
// - https://console.spec.whatwg.org/#inspection
// NOTE: When Chrome logs "Symbol(Symbol.toPrimitive)", that corisponds to
// a funciton on an object set to Symbol.toPrimitive. For example:
//
// ```
// { [Symbol.toPrimitive]: () => null }
// ```
{
console.clear()
let TargetProto = {
'type': 'TargetProto',
toString: () => 'TargetProto.toString()',
valueOf: () => 'TargetProto.valueOf()',
}
let handler = {
get: (target, property, receiver) => {
console.debug('> handler.get()', property, typeof property)
if (!(property in target) && typeof property === 'string') {
console.debug(`> handler.get(): Proxying '${property}'`)
target[property] = new Dynamic()
}
return target[property]
},
// Only used when the object being proxied is a function. Originally I was hoping
// it would be used when a property was called as a function.
apply: (target, thisArg, argumentsList) => {
console.debug('> handler.apply(): ', argumentsList.join(", "))
return target.apply(thisArg, argumentsList)
},
}
// Creates an object who's properties will be dynamically generated
function Dynamic() {
return new Proxy(Object.create(TargetProto), handler)
}
Dynamic.prototype.toString = function toString() { '> Dynamic.toString()' }
Dynamic.prototype.valueOf = function valueOf() { '> Dynamic.valueOf()' }
// -------------------------------------------------------------------------
// NOTE: The following conventions are used to help distinguish log messages
//
// :: - Denotes the beginning of a new test case. Should be followed by a label
// > - Helps track the proxy call sequence that leads to the final result
// - Unprefixed log lines indicate what was returned by the lookup/function call
console.log(':: Create Dynamic instance') // :: Create Dynamic instance
var d = Dynamic()
// NOTE: We get an inspectable "Proxy {}" in our output because that's what
// console.log prints. Unfortunately we have no control over how console.log
// handles printing, as noted in the WHATWG spec:
//
// https://console.spec.whatwg.org/#inspection
console.log(':: console.log(foo.bar.baz)') // :: console.log(foo.bar.baz)
console.log(d.foo.bar.baz) // > handler.get() foo string
// > handler.get() bar string
// > handler.get() baz string
// Proxy {}
console.log(':: console.log(+d)') // :: console.log(+d)
console.log(+d) // > handler.get() Symbol(Symbol.toPrimitive) symbol
// > handler.get() valueOf string
// NaN
//
// NOTE: Unary plus attempts to convert the value returned
// by the `valueOf()` call ("TargetProto.valueOf()")
// into a number, thus final result of NaN.
console.log(':: console.log(d.toString())') // :: console.log(d.toString())
console.log(d.toString()) // > handler.get() toString string
// TargetProto.toString()
console.log(':: console.log(d.valueOf())') // :: console.log(d.valueOf())
console.log(d.valueOf()) // > handler.get() valueOf string
// TargetProto.valueOf()
/*
// Alerts are anoying. Only reason this is here is 'cause back when we used to
// debut with alerts and they coerce vars into strings
console.log(':: alert(d)') // :: alert(d)
alert(d) // > handler.get() Symbol(Symbol.toPrimitive) symbol
// > handler.get() toString string
// TargetProto.toString()
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment