详解JS中统计函数执行次数与执行时间


Posted in Javascript onSeptember 04, 2018

一、统计函数执行次数

常规的方法可以使用 console.log 输出来肉眼计算有多少个输出

不过在Chrome中内置了一个 console.count 方法,可以统计一个字符串输出的次数。我们可以利用这个来间接地统计函数的执行次数

function someFunction() {
  console.count('some 已经执行');
}

function otherFunction() {
  console.count('other 已经执行');
}

someFunction(); // some 已经执行: 1
someFunction(); // some 已经执行: 2
otherFunction(); // other 已经执行: 1

console.count(); // default: 1
console.count(); // default: 2

不带参数则为 default 值,否则将会输出该字符串的执行次数,观测起来还是挺方便的

当然,除了输出次数之外,还想获取一个纯粹的次数值,可以用装饰器将函数包装一下,内部使用对象存储调用次数即可

var getFunCallTimes = (function() {
  
  // 装饰器,在当前函数执行前先执行另一个函数
  function decoratorBefore(fn, beforeFn) {
    return function() {
      var ret = beforeFn.apply(this, arguments);

      // 在前一个函数中判断,不需要执行当前函数
      if (ret !== false) {
        fn.apply(this, arguments);
      }
    };
  }
  
  // 执行次数
  var funTimes = {};
  
  // 给fun添加装饰器,fun执行前将进行计数累加
  return function(fun, funName) {
    // 存储的key值
    funName = funName || fun;
    
    // 不重复绑定,有则返回
    if (funTimes[funName]) {
      return funTimes[funName];
    }
    
    // 绑定
    funTimes[funName] = decoratorBefore(fun, function() {
      // 计数累加
      funTimes[funName].callTimes++;

      console.log('count', funTimes[funName].callTimes);
    });
    
    // 定义函数的值为计数值(初始化)
    funTimes[funName].callTimes = 0;

    return funTimes[funName];
  }
})();
function someFunction() {
  
}

function otherFunction() {
  
}


someFunction = getFunCallTimes(someFunction, 'someFunction');

someFunction(); // count 1
someFunction(); // count 2
someFunction(); // count 3
someFunction(); // count 4

console.log(someFunction.callTimes); // 4



otherFunction = getFunCallTimes(otherFunction);
otherFunction(); // count 1
console.log(otherFunction.callTimes); // 1

otherFunction(); // count 2
console.log(otherFunction.callTimes); // 2

二、统计函数执行时间

Chrome中内置了 console.time 和 console.timeEnd 来打点计算时间

console.time();

for (var i = 0; i < 100000; ++i) {

}

console.timeEnd(); // default: 1.77197265625ms

不传入参数的话,将以default输出毫秒值

我们可以封装一下,传入函数名称,类似上面的做法,使用装饰器在函数执行前后进行处理

var getFunExecTime = (function() {
  
  // 装饰器,在当前函数执行前先执行另一个函数
  function decoratorBefore(fn, beforeFn) {
    return function() {
      var ret = beforeFn.apply(this, arguments);

      // 在前一个函数中判断,不需要执行当前函数
      if (ret !== false) {
        fn.apply(this, arguments);
      }
    };
  }

  // 装饰器,在当前函数执行后执行另一个函数
  function decoratorAfter(fn, afterFn) {
    return function() {
      fn.apply(this, arguments);
      afterFn.apply(this, arguments);
    };
  }
  
  // 执行次数
  var funTimes = {};
  
  // 给fun添加装饰器,fun执行前后计时
  return function(fun, funName) {
    return decoratorAfter(decoratorBefore(fun, function() {
      // 执行前
      console.time(funName);
    }), function() {
      // 执行后
      console.timeEnd(funName);
    });
  }
})();

那么调用的时候,就不需要理会如何计时了

function someFunction() {
  for (var i = 0; i < 100000; ++i) {

  }
}


function otherFunction() {
  for (var i = 0; i < 10000000; ++i) {

  }
}

someFunction = getFunExecTime(someFunction, 'someFunction');
someFunction(); // someFunction: 1.616943359375ms

otherFunction = getFunExecTime(otherFunction, 'otherFunction');
otherFunction(); // otherFunction: 18.157958984375ms

Chrome的Console API毕竟不是标准的,除了使用它之外,还可以选择日期插件 Date 中的 getTime now 相关方法

然而使用Date对象来计算耗时并不正统,推荐使用标准的 performance.now

var start = performance.now();

console.time();

for (var i = 0; i < 10000000; ++i) {

}

var end = performance.now();

console.timeEnd(); // default: 23.598876953125ms

console.log(end - start); // 23.600000015459955

可以看到,它们是相差不大的

使用类似的方法,将它包装起来以便方便调用

var getFunExecTime = (function() {
  
  // 装饰器,在当前函数执行前先执行另一个函数
  function decoratorBefore(fn, beforeFn) {
    return function() {
      var ret = beforeFn.apply(this, arguments);

      // 在前一个函数中判断,不需要执行当前函数
      if (ret !== false) {
        fn.apply(this, arguments);
      }
    };
  }

  // 装饰器,在当前函数执行后执行另一个函数
  function decoratorAfter(fn, afterFn) {
    return function() {
      fn.apply(this, arguments);
      afterFn.apply(this, arguments);
    };
  }
  
  // 执行次数
  var funTimes = {};
  
  // 给fun添加装饰器,fun执行前后计时
  return function(fun, funName) {
    funName = funName || fun;

    if (funTimes[funName]) {
      return funTimes[funName];
    }
    
    // 绑定
    funTimes[funName] = decoratorAfter(decoratorBefore(fun, function() {
      // 执行前
      funTimes[funName].timestampStart = performance.now();
    }), function() {
      // 执行后
      funTimes[funName].timestampEnd = performance.now();
      
      // 将执行耗时存入
      funTimes[funName].valueOf = function() {
        return this.timestampEnd - this.timestampStart;
      };
    });

    return funTimes[funName];
  }
})();
function someFunction() {
  for (var i = 0; i < 100000; ++i) {

  }
}


function otherFunction() {
  for (var i = 0; i < 10000000; ++i) {

  }
}

// 包装
someFunction = getFunExecTime(someFunction);
// 执行
someFunction();
// 获取耗时,可直接使用函数的 valueOf
console.log(+someFunction); // 2.0999999847263098

otherFunction = getFunExecTime(otherFunction, 'otherFunction');
otherFunction(); 
console.log(+otherFunction); // 21.00000000745058

三、如何控制函数的调用次数

也可以通过闭包来控制函数的执行次数

function someFunction() {
  console.log(1);
}

function otherFunction() {
  console.log(2);
}


function setFunCallMaxTimes(fun, times, nextFun) {
  return function() {
    if (times-- > 0) {
      // 执行函数
      return fun.apply(this, arguments);
    } else if (nextFun && typeof nextFun === 'function') {
      // 执行下一个函数
      return nextFun.apply(this, arguments);
    }
  };
}

var fun = setFunCallMaxTimes(someFunction, 3, otherFunction);

fun(); // 1
fun(); // 1
fun(); // 1
fun(); // 2
fun(); // 2

四、如何控制函数的执行时间

因为JS是单线程的,控制函数的执行时间相对来说挺麻烦

通过 async await yield 等异步特性,也许还是能办到的

在React 16中的 Fiber 机制,在某种意义上是能控制函数的执行时机,得空再去看看它是怎么实现的吧

Javascript 相关文章推荐
修改好的jquery滚动字幕效果实现代码
Jun 22 Javascript
js Array对象的扩展函数代码
Apr 24 Javascript
JS交换变量的方法
Jan 21 Javascript
javascript实现列表滚动的方法
Jul 30 Javascript
轻松学习jQuery插件EasyUI EasyUI创建RSS Feed阅读器
Nov 30 Javascript
AngularJS实现星星等级评分功能
Sep 24 Javascript
javascript十六进制数字和ASCII字符之间的转换方法
Dec 27 Javascript
详解vue组件化开发-vuex状态管理库
Apr 10 Javascript
面包屑导航详解
Dec 07 Javascript
Selenium执行Javascript脚本参数及返回值过程详解
Apr 01 Javascript
ES5新增数组的实现方法
May 12 Javascript
vue项目打包后提交到git上为什么没有dist这个文件的解决方法
Sep 16 Javascript
Vue组件中的data必须是一个function的原因浅析
Sep 03 #Javascript
ES6中let 和 const 的新特性
Sep 03 #Javascript
Angular项目如何升级至Angular6步骤全纪录
Sep 03 #Javascript
vue 中滚动条始终定位在底部的方法
Sep 03 #Javascript
前后端如何实现登录token拦截校验详解
Sep 03 #Javascript
vue移动端监听滚动条高度的实现方法
Sep 03 #Javascript
vue 纯js监听滚动条到底部的实例讲解
Sep 03 #Javascript
You might like
php过滤htmlspecialchars() 函数实现把预定义的字符转换为 HTML 实体用法分析
2019/06/25 PHP
Laravel 微信小程序后端搭建步骤详解
2019/11/26 PHP
yii 框架实现按天,月,年,自定义时间段统计数据的方法分析
2020/04/04 PHP
js中获取事件对象的方法小结
2011/03/13 Javascript
文本框回车提交与禁止提交示例
2013/09/27 Javascript
jQuery选择器全面总结
2014/01/06 Javascript
JavaScript数组常用方法
2015/03/02 Javascript
基于javascript实现全屏漂浮广告
2016/03/31 Javascript
详解JavaScript异步编程中jQuery的promise对象的作用
2016/05/03 Javascript
BootStrap3学习笔记(一)之网格系统
2016/05/20 Javascript
原生js实现查询天气小应用
2016/12/09 Javascript
js实现延迟加载的几种方法
2017/04/24 Javascript
基于JavaScript实现数码时钟效果
2020/03/30 Javascript
Vue from-validate 表单验证的示例代码
2017/09/26 Javascript
详解性能更优越的小程序图片懒加载方式
2018/07/18 Javascript
使用vue自定义指令开发表单验证插件validate.js
2019/05/23 Javascript
微信小程序button标签open-type属性原理解析
2020/01/21 Javascript
[04:29]2014DOTA2国际邀请赛 主赛事第三日TOPPLAY
2014/07/21 DOTA
[01:03:09]完美世界DOTA2联赛PWL S2 Forest vs SZ 第二场 11.25
2020/11/26 DOTA
Python translator使用实例
2008/09/06 Python
python实现井字棋游戏
2020/03/30 Python
详解使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)
2020/04/01 Python
iPython pylab模式启动方式
2020/04/24 Python
基于pandas向csv添加新的行和列
2020/05/25 Python
Book Depository欧盟:一家领先的国际图书零售商
2019/05/21 全球购物
linux面试题参考答案(1)
2016/01/22 面试题
易程科技软件测试笔试
2013/03/24 面试题
大学系主任推荐信范文
2013/12/24 职场文书
转党组织关系介绍信
2014/01/08 职场文书
2014年小班元旦活动方案
2014/02/16 职场文书
简单的个人租房协议书范本
2014/11/26 职场文书
2014年银行信贷员工作总结
2014/12/08 职场文书
总结Python使用过程中的bug
2021/06/18 Python
MySQL利用UNION连接2个查询排序失效详解
2021/11/20 MySQL
详解Python中*args和**kwargs的使用
2022/04/07 Python
MySQL数据库 任意ip连接方法
2022/05/20 MySQL