【问题标题】:Dynamic Function Watcher in JSJS 中的动态函数观察器
【发布时间】:2022-10-19 22:07:54
【问题描述】:

我正在做一个宠物项目,一个面向学生的小型前端库。它读取 JS 文件中的变量/代码并对其进行测试,输出一些面板。代码本身大致遵循 Jest 框架。

我的问题是我正在尝试创建一个函数来监视其他函数的执行,对它们进行计数,并让我访问计数。

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
        }
    }
}

这似乎适用于Number()parseInt() 之类的方法,但我不知道如何访问Math.floor() 之类的方法或Array.prototype.map() 之类的原型方法。

我试过传入函数引用而不是使用window["funcNameString"],但这似乎不起作用。

有没有人有关于包装函数或观看这样的函数的建议或提示?

【问题讨论】:

  • 你能分享一下这个函数是如何被调用的,以及它是如何获取它正在监视的函数的吗?

标签: javascript function wrapper


【解决方案1】:

您是指实例或对象的方法吗?一种方法是创建一个新函数。例如

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
        }
    }

} 

尽管使用更多嵌套方法添加对链接方法的支持会变得困难,但是您可以为 functionName name 传递一个字符串并将其拆分为函数的每一层调用实例并重复上述逻辑。

【讨论】:

  • 我认为这也可能是一个合理的解决方案,我会尽快对其进行测试,看看它是否适合我的情况。老实说,我认为只有 3 个级别的深度可能就足够了。这将涵盖 String()、Math.round() 和 Array.prototype.map()。这就是我想要涵盖的所有内容。
  • 这也适用于两个测试用例 parseInt 和 Math.random。我目前的麻烦是试图包装类方法,如 Array.map。有任何想法吗?
【解决方案2】:

那是你要的吗?我还可以为这个函数编写一个块,以便它确定传入的是一个对象还是一个字符串。如果字符串 -> 在窗口上运行此函数作为属性“objectThatStoresFunction”。

我尝试过使用Function.prototype,但它并没有真正起作用。所以这个函数变得有点复杂。

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());

// 1
Math.round(99666.9999999);
console.log(mathRoundWatcher.getCount());

// 2
Math.round(999999999.99);
console.log(mathRoundWatcher.getCount());

也适用于窗口属性和 Array.prototype.map

function watchFunction(objectThatStoresFunction, functionName, optionalOriginalFunction) {
const self = this;
  if (optionalOriginalFunction) {
    objectThatStoresFunction = this.window;
    functionName = optionalOriginalFunction.name;
  }
  let counter = 0;

  const originalFunction = objectThatStoresFunction[functionName] || optionalOriginalFunction;


  objectThatStoresFunction[functionName] = (...args) => {
    counter += 1;
    return originalFunction.bind(self)(...args);
  }

  return {
    // should it remove the watcher or reset the count?
    reset: () => /* reset the count: counter = 0; */ objectThatStoresFunction[functionName] = originalFunction,
    getCount: () => {
      return counter;
    }
  }
}



const arrayMapWatcher = watchFunction(Array.prototype, 'map');

// 0
console.log('Array.prototype.map', arrayMapWatcher.getCount());

[-99].map(() => {});

// 1
console.log('Array.prototype.map', arrayMapWatcher.getCount());

const mathRoundWatcher = watchFunction(Math, 'round');

// 0
console.log('Math.round', mathRoundWatcher.getCount());

// 1
Math.round(99666.9999999);
console.log('Math.round', mathRoundWatcher.getCount());

// 2
Math.round(999999999.99);
console.log('Math.round', mathRoundWatcher.getCount());


const alertWatcher = watchFunction(null, null, window.alert);

// 0
console.log('window.alert', alertWatcher.getCount());

// 1
window.alert('1');
console.log('window.alert', alertWatcher.getCount());

// 2
alert('2')
console.log('window.alert', alertWatcher.getCount());

// reset the alertWatcher counter


alertWatcher.reset();

https://jsfiddle.net/ctbjnawz/3/

【讨论】:

  • 哦,这很聪明。我会尽快尝试一下,看看它是否符合我所有的测试用例!
  • 所以这大多作品。我的目标是能够观察以下函数: parseInt Math.random Array.prototype.map 这个解决方案适用于前两个,因为它们是对象的方法。 Array.map 有一些困难,因为它是一个类方法。这似乎不是该类型功能的解决方案。我会继续修补它。
  • 我会考虑如何解决这个问题,将编辑答案并让你知道我是否找到了解决方案
  • 请看我更新的答案
猜你喜欢
  • 1970-01-01
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-23
  • 2018-11-18
  • 2019-11-02
  • 2014-06-23
相关资源
最近更新 更多