JavaScript中try catch语句的性能分析

JavaScript中try catch语句的性能分析

感谢我上一个博客 Array.prototype.forEach 方法详解 提出了两种方法来打破 forEach 循环。
但是网上的博客几乎只提到了第一种方法(try catch + throw 方法),而第二种方法(splice + return 方法)是在了解了forEach方法的原理后才想到的。
所以想比较这两种方法的性能,所以需要比较splice方法和try-catch语句的性能。

 //方法一:尝试catch + throw方法  
 常量 arr = [ 1, 2, 3, 4];  
 尝试 {  
 arr。 forEach( ( 项目 ) => {  
 如果(项目 === 2){  
 throw new Error( ` value is ${item} Break out forEac loop when `);  
 }  
 安慰。日志(项目); // 只打印 1  
 });  
 } 抓住 (e) {  
 安慰。日志(e); //错误:值为2时跳出forEac循环  
 }  
 安慰。日志(arr); // [1, 2, 3, 4]  
 复制代码 //方法二:拼接+返回方法  
 //迭代时使用splice方法从数组中移除元素  
 常量 arr = [ 1, 2, 3, 4];  
 让 spliceArr = null;  
 arr。 forEach((项目,索引)=> {  
 如果(项目 === 2){  
 spliceArr = arr。拼接(索引); // 删除2之后的所有元素并赋值给spliceArr  
 返回;  
 }  
 安慰。日志(项目); // 1  
 });  
 arr。 splice(arr.length, spliceArr.length, ...spliceArr); // 缝合删除的元素  
 安慰。日志(arr); // [1, 2, 3, 4]  
 复制代码

一、拼接方法的基本介绍及原理

拼接方法可以参考以下两篇博客。

看了下splice方法的源码,发现在splice方法内部调用了一个循环。
所以如果我们简单地用大O符号来分析:

  • 方法一:try catch + throw 的性能是O(n)。
  • 方法2:拼接+返回的性能是O(n²)。
    但是查阅了相关资料后发现try catch语句也是一个非常消耗性能的语句,所以我们需要对try catch语句进行分析。

2.try catch语句的性能分析

try catch 语句可以参考以下三篇博客。

我用的分析方法主要是参考 JS中使用try catch对代码运行的性能影响分析 博客中的方法分为五种方法来讨论。
这篇博客的结果是:当运行环境是Chrome51时,得到的 结果是98.2ms; 1026.9 毫秒; 107.7; 1028.5毫秒; 105.9 毫秒 .
但与我自己实验的结果不同,可能是版本问题,现代浏览器会优化我们的代码。
在我的结果下面

2.1 运行环境:Node.js v16.16.0

测试代码如下:

 //空白组1:[不使用try catch对数据取模需要1000万次]  
 !( 功能 () {  
 //耗时不try catch  
 var t = 新日期(); // 启动耗时代码  
 for ( var i = 0; i < 100000000; i++) {  
 变量 p = i % 2;  
 }  
 安慰。日志(“1:”,新日期() - t); //耗时代码结束  
 })();  
  
 //参考组2:【用try包围耗时代码,内联耗时代码】  
 !( 功能 () {  
 // try中内联代码的耗时情况  
 var t = 新日期(); // 启动耗时代码  
 尝试 {  
 for ( var i = 0; i < 100000000; i++) {  
 变量 p = i % 2;  
 }  
 抛出新的错误();  
 } 捕捉 (e) {}  
 安慰。日志(“2:”,新日期() - t); //耗时代码结束  
 })();  
  
 //参考组3:【用try包围耗时代码,将耗时代码外化】  
 !( 功能 () {  
 // try中内联代码的耗时情况  
 var t = 新日期(); // 启动耗时代码  
 函数运行(){  
 for ( var i = 0; i < 100000000; i++) {  
 变量 p = i % 2;  
 }  
 }  
 尝试 {  
 跑();  
 抛出新的错误();  
 } 捕捉 (e) {}  
 安慰。日志(“3:”,新日期() - t); //耗时代码结束  
 })();  
  
 //参考组4:【用catch包围耗时代码,内联耗时代码】  
 !( 功能 () {  
 //catch中耗时的内联代码  
 var t = 新日期(); // 启动耗时代码  
 尝试 {  
 抛出新的错误();  
 } 抓住 (e) {  
 for ( var i = 0; i < 100000000; i++) {  
 变量 p = i % 2;  
 }  
 }  
 安慰。日志(“4:”,新日期() - t); //耗时代码结束  
 })();  
  
 //参考第5组:【用catch包围耗时代码,将耗时代码外化】  
 !( 功能 () {  
 函数运行(){  
 for ( var i = 0; i < 100000000; i++) {  
 变量 p = i % 2;  
 }  
 }  
 //catch中耗时的内联代码  
 var t = 新日期(); // 启动耗时代码  
 尝试 {  
 抛出新的错误();  
 } 抓住 (e) {  
 跑();  
 }  
 安慰。日志(“5:”,新日期() - t); //耗时代码结束  
 })();  
 复制代码

五种方法的结果:都在60-80ms之类的 ,所以它可能是由现代浏览器优化我们的代码引起的。

2.2 运行环境:Chrome版(105.0.5195.127)或Microsoft Edge版(105.0.1343.42)

测试代码如下:

 <!DOCTYPE html >  
 < html lang = "en" >  
 <头>  
 <元字符集=“UTF-8”/>  
 < meta http-equiv = "X-UA-Compatible" 内容 = "IE=edge" />  
 <元名称=“视口”内容=“宽度=设备宽度,初始比例=1.0” />  
 < title >1.无 try catch 的情况下 </ title >  
 </ head >  
 <身体>  
 < h1 id = "h1" ></ h1 >  
 <脚本>  
 //空白组1:[不使用try catch对数据取模需要1000万次] !( function ( ) { //不使用try catch耗时  
 常量 h1 = 文档。查询选择器(“#h1”); var t = 新日期 (); // 启动耗时代码  
 for ( var i = 0 ; i 100000000 ; i++) { var p = i % 2 ; } h1。 innerText = `案例1:消费性能为${ new Date() - t} ms` ; //耗时代码结束 })();</ script >  
 </ body >  
 </ html >  
 复制代码 <!DOCTYPE html >  
 < html lang = "en" >  
 <头>  
 <元字符集=“UTF-8”/>  
 < meta http-equiv = "X-UA-Compatible" 内容 = "IE=edge" />  
 <元名称=“视口”内容=“宽度=设备宽度,初始比例=1.0” />  
 < title >2.将耗时代码用 try 包围,内联耗时代码 </ title >  
 </ head >  
 <身体>  
 < h1 id = "h1" ></ h1 >  
 <脚本>  
 //参考组2:[用try包围耗时代码,内联耗时代码] !( function ( ) { // try中内联代码耗时情况  
 常量 h1 = 文档。查询选择器(“#h1”); var t = 新日期 (); // 启动耗时代码  
 尝试 { for ( var i = 0 ; i 100000000 ; i++) { var p = i % 2 ; } 抛出新的错误(); } 捕捉 (e) {} h1. innerText = `案例2:消费性能为${ new Date() - t} ms` ; //耗时代码结束 })();</ script >  
 </ body >  
 </ html >  
 复制代码 <!DOCTYPE html >  
 < html lang = "en" >  
 <头>  
 <元字符集=“UTF-8”/>  
 < meta http-equiv = "X-UA-Compatible" 内容 = "IE=edge" />  
 <元名称=“视口”内容=“宽度=设备宽度,初始比例=1.0” />  
 < title >3.将耗时代码用 try 包围,外联耗时代码 </ title >  
 </ head >  
 <身体>  
 < h1 id = "h1" ></ h1 >  
 <脚本>  
 //参考组3:[用try包围耗时代码,将耗时代码外化] !( function ( ) { //try中内联函数的耗时情况  
 常量 h1 = 文档。查询选择器(“#h1”); var t = 新日期 (); // 启动耗时代码  
 函数运行 ( ) { for ( var i = 0 ; i 100000000 ; i++) { var p = i % 2 ; } } 尝试 { 运行 ();抛出新的错误(); } 捕捉 (e) {} h1. innerText = `案例3:消费性能为${ new Date() - t} ms` ; //耗时代码结束 })();</ script >  
 </ body >  
 </ html >  
 复制代码 <!DOCTYPE html >  
 < html lang = "en" >  
 <头>  
 <元字符集=“UTF-8”/>  
 < meta http-equiv = "X-UA-Compatible" 内容 = "IE=edge" />  
 <元名称=“视口”内容=“宽度=设备宽度,初始比例=1.0” />  
 < title >4.将耗时代码用 catch 包围,内联耗时代码 </ title >  
 </ head >  
 <身体>  
 < h1 id = "h1" ></ h1 >  
 <脚本>  
 //参考组4:[用catch包围耗时代码,内联耗时代码] !( function ( ) { //catch中耗时内联代码  
 常量 h1 = 文档。查询选择器(“#h1”); var t = 新日期 (); // 启动耗时代码  
 尝试{抛出新错误(); } 捕捉 (e) { for ( var i = 0 ; i 100000000 ; i++) { var p = i % 2 ; } } h1。 innerText = `案例4:消费性能为${ new Date() - t} ms` ; //耗时代码结束 })();</ script >  
 </ body >  
 </ html >  
 复制代码 <!DOCTYPE html >  
 < html lang = "en" >  
 <头>  
 <元字符集=“UTF-8”/>  
 < meta http-equiv = "X-UA-Compatible" 内容 = "IE=edge" />  
 <元名称=“视口”内容=“宽度=设备宽度,初始比例=1.0” />  
 < title >5.将耗时代码用 catch 包围,外联耗时代码 </ title >  
 </ head >  
 <身体>  
 < h1 id = "h1" ></ h1 >  
 <脚本>  
 //参考第5组:[用catch包围耗时代码,将耗时代码外化] !( function ( ) { //catch中的耗时内联代码  
 常量 h1 = 文档。查询选择器(“#h1”); var t = 新日期 (); // 启动耗时代码  
 函数运行 ( ) { for ( var i = 0 ; i 100000000 ; i++) { var p = i % 2 ; } } 尝试 { 抛出新的错误 (); } 捕捉 (e) { 运行 (); } h1。 innerText = `案例5:消费性能为${ new Date() - t} ms` ; //耗时代码结束 })();</ script >  
 </ body >  
 </ html >  
 复制代码

这五种方法的结果也相似。第一次运行需要100ms以上,但是刷新页面大概需要60-80ms,和node.js环境差不多。

3. 两种跳出forEach循环的方法性能对比

运行代码如下:

 //方法一:尝试catch + throw方法  
 !( 功能 () {  
 常量 arr = 新数组(100000)。填充(0)。地图((项目,索引)=>索引+ 1);  
 安慰。时间(“testTime1”); //启动定时器  
 // 常量开始 = Date.now();  
 尝试 {  
 arr。 forEach( ( 项目 ) => {  
 如果(项目 === 50000){  
 投掷1;  
 }  
 });  
 } 捕捉 (e) {}  
 安慰。 timeEnd("testTime1"); //结束时间  
 // console.log("runtime:", Date.now() - start);  
 })();  
 复制代码 //方法二:拼接+返回方法  
 !( 功能 () {  
 常量 arr = 新数组(100000)。填充(0)。地图((项目,索引)=>索引+ 1);  
 安慰。时间(“testTime2”); //启动定时器  
 // 常量开始 = Date.now();  
 //当数组元素过多时,会报错RangeError: Maximum call stack size exceeded  
 让 spliceArr = null;  
 arr。 forEach((项目,索引)=> {  
 如果(项目 === 50000){  
 spliceArr = arr。拼接(索引); // 删除2之后的所有元素并赋值给spliceArr  
 返回;  
 }  
 });  
 arr。 splice(arr.length, spliceArr.length, ...spliceArr); // 缝合删除的元素  
 安慰。 timeEnd("testTime2"); //结束时间  
 // console.log("runtime:", Date.now() - start);  
 })();  
 复制代码

结果如下

第一种方法比第二种方法好,但是 间隙尺寸 经过 跳出循环条件数组大小 影响,但第一种方法比第二种方法好。

结语

对单词进行编码并不容易。觉得有帮助的朋友会喜欢并关注。

这是迄今为止我所知道的最好的答案,但当然也存在一些误解。

所以如果你对本文有任何疑问,可以在评论区留言。欢迎大家指出文中的错误观点。

版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议。转载请附上原文出处链接和本声明。

这篇文章的链接: https://homecpp.art/2620/9601/1758

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/38260/15032111

posted @ 2022-09-21 11:15  哈哈哈来了啊啊啊  阅读(255)  评论(0编辑  收藏  举报