Last active
February 28, 2020 14:45
-
-
Save chamberlainpi/6ea7de99b47619545727ed6f574d7017 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/** | |
@author Pierre Chamberlain | |
@description This is a demo of using the 'proxyFieldsOf(...)' method. | |
*/ | |
const { proxyFieldsOf } = require('./proxy-fields-of'); | |
const trace = console.log.bind(console); | |
const SOME_NAMESPACE = { | |
someObject: { | |
hi(name) { | |
trace(`Hello "${name}"!`); | |
return 'foobar'; | |
} | |
} | |
}; | |
const PROXY_NAMESPACE = proxyFieldsOf(SOME_NAMESPACE, { | |
fullpath: 'SOME_NAMESPACE', | |
recursive: true, | |
getter({ target, prop, fullpath }) { | |
trace(`*PROXY* getter: ${prop}`); | |
}, | |
before({ target, args, method, fullpath }) { | |
trace(`*PROXY* calling: ${fullpath}.${method}( ... [${args.length} args])`); | |
}, | |
after({ target, args, method, fullpath, result }) { | |
trace(`*PROXY* called: ${fullpath}.${method}( ... [${args.length} args]) with result of: ` + result); | |
} | |
}); | |
PROXY_NAMESPACE.someObject.hi('Bob'); | |
/* =========== Outputs: ============ | |
*PROXY* getter: someObject | |
*PROXY* calling: SOME_NAMESPACE.someObject.hi( ... [1 args]) | |
Hello "Bob"! | |
*PROXY* called: SOME_NAMESPACE.someObject.hi( ... [1 args]) with result of: foobar | |
=================================== */ |
This file contains hidden or 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
/** | |
@author Pierre Chamberlain | |
@description This is the 'proxyFieldsOf(...)' method, used for inspecting when an object's fields | |
are being accessed (getter) and when functions are being invoked (before & after its execution). | |
It can also recursively inspect all of the target's child-objects (and their grand-children, etc.) | |
FIXED: Getters were only called once (the first time), should be every time they're accessed now. | |
*/ | |
const SELF = module.exports = { | |
proxyFieldsOf(target, options) { | |
const proxyCache = {}; | |
if (typeof options === 'function') options = { after: options }; | |
return new Proxy(proxyCache, { | |
get(obj, prop) { | |
if (prop in proxyCache) { | |
var proxy = proxyCache[prop]; | |
return proxy._isGetter ? proxy.callGetter() : proxy; | |
} | |
const valueOrFunc = target[prop]; | |
const valueType = typeof valueOrFunc; | |
if (valueType !== 'function') { | |
const getterProxy = { _isGetter: true, callGetter: null }; | |
//For deep objects (more elaborate APIs), it might make sense to recursively proxy it's child objects: | |
if (valueType === 'object' && options.recursive) { | |
options.fullpath = options.fullpath ? options.fullpath + '.' + prop : prop; | |
getterProxy.childProxy = SELF.proxyFieldsOf(valueOrFunc, options); | |
} | |
getterProxy.callGetter = function () { | |
options.getter && options.getter({ target, prop, fullpath: options.fullpath, result: valueOrFunc }); | |
return getterProxy.childProxy != null ? getterProxy.childProxy : valueOrFunc; | |
}; | |
proxyCache[prop] = getterProxy; | |
return getterProxy.callGetter(); | |
} | |
const methodProxy = function (...args) { | |
const argsNew = { target, args, method: prop, fullpath: options.fullpath, result: null }; | |
options.before && options.before(argsNew); | |
argsNew.result = valueOrFunc.apply(target, args); | |
options.after && options.after(argsNew); | |
return argsNew.result; | |
} | |
proxyCache[prop] = methodProxy; | |
return methodProxy; | |
} | |
}); | |
}, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment