Project: Proxies as Variable Environments
Logbook
Wed Dec 27 10:08:45 PST 2023
When I was writing small scripts, like the "generate new post" script which I used to generate this page, I found I wanted more visibility into what my script was doing. Especially while developing or debugging, I wanted to see every time a variable changed. So I used a Proxy (MDN) to make a special store for special variables which would print whenever they were retrieved or set.
// A proxy to store some special variables with some logging tools (off by default)
// Turn logging on by passing `{ level: Infinity }`
// Override the logger by passing in `{ log: MyLog }`. Defaults to `console.log`
// Level 0: Nothing printed
// Level 1: Sets are pretty-printed
// Level 2: Gets are also pretty-printed
// Level 3: Entire variable storage is pretty-printed JSONified after every Set
// Level 4: Ditto also before every Get
const createStore = ({ log = console.log, level = 0 } = {}) => {
return new Proxy(
{},
{
get(target, prop) {
level >= 4 &&
log?.(`Target is now: \${JSON.stringify(target, null, 2)}`);
level >= 2 &&
log?.(`Getting '\${prop.toString()}' value of '\${target[prop]}'`);
return target[prop];
},
set(target, prop, value) {
level >= 1 &&
log?.(`Setting '\${prop.toString()}' to '\${value}'`)
target[prop] = value;
level >= 3 &&
log?.(`Target is now: \${JSON.stringify(target, null, 2)}`)
}
}
)
}
window.createStore = createStore;
I also made a custom, functional logger which could be disabled.
// A logger with some additional tools to turn it on and off
// Override the internal logger by passing in `{ log: MyLog }`. Defaults to `console.log`
const createFunctionalLogger = ({ log = console.log } = {}) => {
let state = 'on';
return {
on() { state = 'on' },
off() { state = 'off' },
toggle() { state === 'on' ? off() : on() },
async preserveState(fn) {
const previous = state;
await fn();
state = previous;
},
log(...args) {
state === 'on' && log(...args);
}
}
}
window.createFunctionalLogger = createFunctionalLogger;
As a quick example of how to use these together, here's a transcript from a session with them both:
const logger = createFunctionalLogger() // Will use `console.log`
store = createStore({ level: 2, log: logger.log })
store.value = 3 // Logs "Setting 'value' to '3'"
logger.off()
store.value = 6 // No log
logger.on()
store.value = 8 // Logs "Setting 'value' to '8'"
Backlinks
Keywords
-
No keywords