I'm working on a pet project, a little front-end library for students. It reads variables/code in a JS file and tests it, outputting some panels. The code itself roughly follows the Jest framework.
My problem is that I'm trying to create a function that watches the execution of other functions, counts them, and lets me access the count.
function watchFunction(funcName){
let originalFunction = window[funcName];
let counter = 0;
// Wrap the function, counts when called
window[funcName] = function(...args){
console.log("watching");
counter ;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
This seems to work for methods like Number() or parseInt(), but I don't know how I would go about accessing methods like Math.floor(), or prototype methods like Array.prototype.map().
I've tried passing in the function reference instead of using window["funcNameString"], but that doesn't seem to work.
Does anyone have suggestions or tips for wrapping functions or watching functions like this?
CodePudding user response:
Do you mean a method of an instance or object? One way is to create a new function. e.g
function WatchInstanceMethods(instance, functionName){
let originalFunction = window[instance][funcName];
let counter = 0;
window[instance][functionName] = function(...args){
console.log("watching");
counter ;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
although adding support for chaining methods will get difficult with more nested methods but you can pass a string for functionName name and split it to have each layer of calling instance for function and repeat the logic above.
CodePudding user response:
Is that what you want? I can also write a block for this function so that it determines whether an object has been passed in or a string. If string -> run this function on window as a property "objectThatStoresFunction".
I've tried playing around with the Function.prototype
, but it doesn't really work. So the function turned out a bit more complex.
function watchFunction(objectThatStoresFunction, functionName) {
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName];
objectThatStoresFunction[functionName] = (...args) => {
console.log("watching");
counter = 1;
return originalFunction(...args);
}
return {
getCount: () => {return counter}
}
}
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log(mathRoundWatcher.getCount());
Math.round(99666.9999999);
// 1
console.log(mathRoundWatcher.getCount());
Math.round(999999999.99);
// 2
console.log(mathRoundWatcher.getCount());
Works with window properties too
function watchFunction(objectThatStoresFunction, functionName, optionalOriginalFunction) {
if (optionalOriginalFunction) {
objectThatStoresFunction = this.window;
functionName = optionalOriginalFunction.name;
console.log('function name', functionName);
}
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName] ?? optionalOriginalFunction;
objectThatStoresFunction[functionName] = (...args) => {
counter = 1;
return originalFunction(...args);
}
return {
reset: () => objectThatStoresFunction[functionName] = originalFunction,
getCount: () => {return counter}
}
}
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log(mathRoundWatcher.getCount());
Math.round(99666.9999999);
// 1
console.log(mathRoundWatcher.getCount());
Math.round(999999999.99);
// 2
console.log(mathRoundWatcher.getCount());
const alertWatcher = watchFunction(null, null, window.alert);
// 0
console.log(alertWatcher.getCount());
window.alert('1');
// 1
console.log(alertWatcher.getCount());
alert('2')
// 2
console.log(alertWatcher.getCount());