JavaScript函数性能测量方法对比:Performance.now与Console.time
本文对比了JavaScript中测量函数性能的两种主要方式:Performance.now与Console.time。详细解析了两者在精度、浏览器实现差异(如Chrome与Firefox)及防指纹攻击机制上的不同。通过实际代码示例,探讨了多次运行、输入值一致性及跨浏览器引擎对比对测试结果的影响,并提供了节流CPU和定位性能瓶颈的最佳实践建议。
概述
测量函数执行耗时是验证不同实现方案性能优劣的有效手段,同时也能确保代码变更未引入性能回退,并帮助定位系统瓶颈。
优异的性能能提升用户体验,进而提高用户留存率。研究表明,88%的在线消费者在遭遇性能导致的体验不佳后,再次访问的可能性较低。
因此,识别代码瓶颈并量化改进效果至关重要。特别是在开发浏览器端的JavaScript时,需注意其单线程特性,每一行代码的执行都可能阻塞DOM渲染。
本文将介绍如何测量函数性能,以及如何正确解读和处理测量结果。
Performance.now
Performance API通过performance.now()方法提供对DOMHighResTimeStamp的访问,该函数返回自页面加载以来的时间(毫秒为单位),精度最高可达5微秒(以小数表示)。
实际操作中,需记录两个时间戳并保存至变量,通过计算第二个时间戳与第一个时间戳的差值来获取耗时。
const t0 = performance.now();
for (let i = 0; i < array.length; i++) {
// some code
}
const t1 = performance.now();
console.log(t1 - t0, 'milliseconds');
Chrome输出
0.6350000001020817 "milliseconds"
Firefox输出
1 milliseconds
可见Firefox的结果与Chrome存在显著差异,这是因为从Firefox 60版本开始,该浏览器将performance API的精度降低至2毫秒。
Performance API的功能远不止返回时间戳,它还支持测量导航计时、用户计时和资源计时。如需更详细解释,可参考相关文档。
但在本用例中,我们仅需测量单个函数的性能,因此使用时间戳已足够。
那不是和Date.now一样吗?
此时你可能会想到:我也可以使用Date.now来实现这一功能。
确实可行,但存在局限性。
Date.now返回的是自Unix纪元("1970-01-01-01T00:00:00:00Z")以来的毫秒数,且依赖于系统时钟。这不仅意味着精度较低,而且时间值未必单调递增。WebKit工程师Tony Gentilcore对此解释如下:
较少被考虑到的是,基于系统时间的Date并非监控用户行为的理想选择。大多数系统会运行守护进程定期同步时间,时钟每隔15-20分钟可能会调整几毫秒。在此频率下,约1%的10秒时间间隔是不准确的。
Console.time
该API使用简便,只需在待测代码前放置console.time,在代码后放置console.timeEnd,并确保两者使用相同的字符串参数即可。一页上最多可同时运行10,000个计时器。
其精度与Performance API相同,但具体表现仍取决于浏览器实现。
console.time('test');
for (let i = 0; i < array.length; i++) {
// some code
}
console.timeEnd('test');
这将自动生成易于理解的输出,例如:
Chrome输出
test: 0.766845703125ms
Firefox输出
test: 2ms - timer ended
此处的输出结果与Performance API非常相似。
Console.time的优势在于使用便捷,无需手动计算两个时间戳之间的差值。
缩短时间精度
若在不同浏览器中使用上述API测量函数,可能会发现结果存在差异。
这是浏览器为防止定时攻击和指纹识别而采取的措施,因为过于精确的时间戳可能被黑客用于识别用户身份。
例如,Firefox等浏览器通过降低精度至2毫秒(自版本60起)来防范此类风险。
需要注意的事项
现在你已掌握测量JavaScript函数速度的工具,但最好避免一些常见陷阱。
分而治之
当你发现过滤某些结果时速度较慢,但无法确定具体瓶颈所在。
与其盲目猜测代码中哪部分运行缓慢,不如利用上述函数进行测量。
要追踪问题,首先将console.time语句包裹在慢速代码块周围。接着测量各部分的执行时间,若某部分比其他部分更慢,则继续深入该部分,直至找到瓶颈。
计时语句之间的代码越少,跟踪无关内容的概率就越低。
注意输入值
在实际应用中,给定函数的输入值可能会发生显著变化。仅针对任意随机值测量函数速度无法提供具有实际参考价值的數據。
务必确保使用相同的输入值运行代码。
多次运行函数
假设你有一个函数遍历数组,对每个值进行计算并返回结果数组。你想知道是forEach还是简单的for循环更有效。
这是函数:
function testForEach(x) {
console.time('test-forEach');
const res = [];
x.forEach((value, index) => {
res.push(value / 1.2 * 0.1);
});
console.timeEnd('test-forEach')
return res;
}
function testFor(x) {
console.time('test-for');
const res = [];
for (let i = 0; i < x.length; i ++) {
res.push(x[i] / 1.2 * 0.1);
}
console.timeEnd('test-for')
return res;
}
你可以这样测试它们:
const x = new Array(100000).fill(Math.random());
testForEach(x);
testFor(x);
如果你在Firefox中运行上述函数,将获得类似以下输出:
test-forEach: 27ms - timer ended
test-for: 3ms - timer ended
看起来forEach变慢了,对吗?
让我们看看是否使用相同的输入两次运行相同的函数:
testForEach(x);
testForEach(x);
testFor(x);
testFor(x);
test-forEach: 13ms - timer ended
test-forEach: 2ms - timer ended
test-for: 1ms - timer ended
test-for: 3ms - timer ended
如果我们第二次调用forEach测试,其性能与for循环相当。鉴于初始值较慢,可能无论如何都不值得使用forEach。
...在多个浏览器中
如果我们在Chrome中运行上述代码,结果突然看起来不同:
test-forEach: 6.156005859375ms
test-forEach: 8.01416015625ms
test-for: 4.371337890625ms
test-for: 4.31298828125ms
这是因为Chrome和Firefox拥有不同的JavaScript引擎,并具备不同类型的性能优化。意识到这些差异非常重要。
在这种情况下,Firefox在相同输入下对forEach进行了较好的优化。
for在两个引擎上的性能均更优,因此最好坚持使用for循环。
这就是为什么要在多个引擎中进行测量的原因。如果仅使用Chrome进行测量,你可能会得出与for相比,forEach表现并不差的错误结论。
节流你的CPU
这些数值看起来并不高。要知道,你的开发机器通常比网站所服务的普通手机浏览器速度快得多。
为了感受这种差异,浏览器提供了一种功能,允许你限制CPU性能。
有了这个功能,那些10或50毫秒的耗时很快会变成500毫秒。
测量相对表现
这些原始结果不仅取决于你的硬件,还取决于你的CPU和JavaScript线程的当前负载。应重点关注测量结果的相对改进,因为下次重启电脑时,这些数字可能会大相径庭。
总结
在本文中,我们探讨了一些可用于测量性能的JavaScript API,以及如何在“真实世界”中应用它们。对于简单测量,我发现使用console.time更为便捷。
我认为许多前端开发人员每天并未给予性能足够的重视,尽管这对收入有直接影响。
以上就是测量JavaScript函数的性能各种方式对比的详细内容,更多关于JavaScript函数性能资料请关注本站其它相关文章!
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
