确定哪些JavaScript函数未被调用

确定哪些JavaScript函数未被调用,javascript,browser,google-chrome-devtools,Javascript,Browser,Google Chrome Devtools,假设您有一个使用一些JavaScript库的网站,无论是您自己的还是第三方编写的。除了最小化和混淆之外,我想知道是否可以通过删除未使用的函数来进一步减小脚本负载的大小。一旦达到一定的复杂程度,就很难/不可能知道在所有可能的执行路径下最终调用了哪些函数。这让我产生了这样一个问题:在使用了一段时间的网页后,是否有任何工具或方法来确定哪些用户定义的函数(不是内置函数)没有被调用 一种明显的可能性是在对注册函数的调用中封装所有函数定义,这1)将函数添加到注册器,2)注入一些代码以将函数标记为已被调用。然

假设您有一个使用一些JavaScript库的网站,无论是您自己的还是第三方编写的。除了最小化和混淆之外,我想知道是否可以通过删除未使用的函数来进一步减小脚本负载的大小。一旦达到一定的复杂程度,就很难/不可能知道在所有可能的执行路径下最终调用了哪些函数。这让我产生了这样一个问题:在使用了一段时间的网页后,是否有任何工具或方法来确定哪些用户定义的函数(不是内置函数)没有被调用

一种明显的可能性是在对注册函数的调用中封装所有函数定义,这1)将函数添加到注册器,2)注入一些代码以将函数标记为已被调用。然后就可以向函数注册器查询那些没有被调用的函数。然而,这种方法极其复杂。最好的办法是在web服务器上编写JavaScript代码解析器,该解析器由运行时标志(“处于JS诊断模式”)启用,该标志捕获所有JavaScript响应并相应修改代码。但不需要太多的想象力就可以意识到这是多么容易出错和困难

更新:我只是想澄清一下,我不是在寻找一种自动删除未使用函数的解决方案,我对此并不满意,因为我担心会带来不稳定性。相反,通过对函数使用情况的开发时分析,我可以选择要删除哪些包含/函数,这样最终的解决方案就可以在发布之前进行适当的测试

一旦达到一定的复杂程度,就很难/不可能知道在所有可能的执行路径下最终调用了哪些函数

没有必要在“困难/不可能”中加斜杠。死代码消除相当于解决停止问题,因此是不可能的

在网页使用一段时间后,是否有任何工具或方法来确定哪些用户定义的函数(不是内置函数)没有被调用

这介于分析(确定代码的哪些部分执行的频率)和覆盖率分析(确定代码的哪些部分执行的频率)之间。更准确地说,这听起来像是在寻找功能覆盖率

请注意,代码覆盖率经常在测试上下文中讨论,以至于对某些人来说,“代码覆盖率”和“测试覆盖率”是同义词,但代码覆盖率与测试无关。它只是指“当我运行特定的工作负载时,代码的哪些部分会被执行?”

对上述问题中的“部分”一词有不同的解释,它们导致了不同类型的报道:

  • 行覆盖率:执行哪些行
  • 函数覆盖率:执行哪些函数?(这就是您感兴趣的。)
  • 语句覆盖率:执行哪些语句
  • 表达式覆盖率:执行哪些表达式
  • 分支覆盖率:执行条件的哪些分支
  • 路径覆盖率:执行通过块的哪些路径
行覆盖率通常没有用处,因为只需重新格式化代码即可更改数字。事实上,在ECMAScript中,我可以轻松地使所有行都通过简单地将所有内容写入一行来执行

分支覆盖率和路径覆盖率之间的差异可以通过以下代码示例:

函数foo(bar,baz){
如果(巴){
控制台日志(“bar”);
}否则{
控制台日志(“无条”);
}
if(baz){
控制台日志(“baz”);
}否则{
控制台日志(“无baz”);
}
}
为了覆盖所有分支机构,我需要两个电话:

foo(真、假);
foo(假、真);
为了覆盖通过函数的所有可能路径,我需要四个调用:

foo(真,真);
foo(真、假);
foo(假、真);
foo(假,假);
如果您基于函数覆盖率进行启发式死代码消除,您将能够删除未使用的函数。如果您根据分支覆盖率进行操作,您甚至可以从
If
switch
语句中删除未使用的分支

如果您正在寻找代码覆盖率工具,您可能必须查看测试空间,因为这是它们最常用的地方。您可能需要编写自己的死代码消除工具,或者修补现有的minifier,以便它可以从代码覆盖率运行中读取日志文件,并据此做出决策


据我所知,虽然我可能错了,但没有现成的工具可以做到这一点。

请参阅。这是一个被称为树摇晃的问题。Webpack包含一些实用程序以方便使用。这是一个很大的主题,有很多资源可以提供帮助。在调用函数时,可以使用要记录的内容覆盖Function.prototype.call。一个简短的实验表明这是可能的。显然,这与最佳实践相反,但W3C并不是我真正的父亲。如果编译代码库中所有函数的列表,则可以减去被调用函数的列表。@CharlesBamford Monkeypatching
Function.prototype.call
不会有帮助,因为函数通常是直接调用的。您必须使用其原型
call
函数调用每个方法。包装所有函数的最简单方法是编写一个babel插件,用于传输代码,包装每个函数。这应该是相当简单的,您不必触及代码库,您可以简单地生成两个版本的代码,一个带有转换,另一个不带转换