详解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 相关文章推荐
整理一些JavaScript的IE和火狐的兼容性注意事项
Mar 17 Javascript
jQuery setTimeout()函数使用方法
Apr 07 Javascript
Jquery实现列表(隔行换色,全选,鼠标滑过当前行)效果实例
Jun 09 Javascript
三种动态加载js的jquery实例代码另附去除js方法
Apr 30 Javascript
告诉你什么是javascript的回调函数
Sep 04 Javascript
js继承call()和apply()方法总结
Dec 08 Javascript
使用JavaScript 实现的人脸检测
Mar 24 Javascript
轻松实现js图片预览功能
Jan 18 Javascript
浅析JS原型继承与类的继承
Apr 07 Javascript
JS组件Bootstrap导航条使用方法详解
Apr 29 Javascript
JS原型与原型链的深入理解
Feb 15 Javascript
快速解决bootstrap下拉菜单无法隐藏的问题
Aug 10 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
ThinkPHP中的系统常量和预定义常量集合
2014/07/01 PHP
PHP移动文件指针ftell()、fseek()、rewind()函数总结
2014/11/18 PHP
php 一维数组的循环遍历实现代码
2017/04/10 PHP
Windows平台实现PHP连接SQL Server2008的方法
2017/07/26 PHP
php用wangeditor3实现图片上传功能
2019/08/22 PHP
php远程请求CURL实例教程(爬虫、保存登录状态)
2020/12/10 PHP
如何在Web页面上直接打开、编辑、创建Office文档
2007/03/12 Javascript
JS弹出层的显示与隐藏示例代码
2013/12/27 Javascript
利用try-catch判断变量是已声明未声明还是未赋值
2014/03/12 Javascript
extjs 分页使用jsp传递数据示例
2014/07/29 Javascript
JavaScript设计模式之单例模式实例
2014/09/24 Javascript
javascript浏览器窗口之间传递数据的方法
2015/01/20 Javascript
html的DOM中document对象forms集合用法实例
2015/01/21 Javascript
jquery自定义插件——window的实现【示例代码】
2016/05/06 Javascript
ionic实现带字的toggle滑动组件
2016/08/27 Javascript
JS DOMReady事件的六种实现方法总结
2016/11/23 Javascript
JS FormData上传文件的设置方法
2017/07/05 Javascript
vue操作下拉选择器获取选择的数据的id方法
2018/08/24 Javascript
vue 项目 iOS WKWebView 加载
2019/04/17 Javascript
python中的代码编码格式转换问题
2015/06/10 Python
Python部署web开发程序的几种方法
2017/05/05 Python
Python实现的快速排序算法详解
2017/08/01 Python
python pandas库的安装和创建
2019/01/10 Python
python实现彩色图转换成灰度图
2019/01/15 Python
对Python3之方法的覆盖与super函数详解
2019/06/26 Python
Pytorch修改ResNet模型全连接层进行直接训练实例
2019/09/10 Python
详解python 破解网站反爬虫的两种简单方法
2020/02/09 Python
Lookfantastic澳大利亚官网:英国知名美妆购物网站
2021/01/07 全球购物
数据库笔试题
2013/05/09 面试题
软件测试工程师面试问题精选
2016/10/28 面试题
超市实习总结自我鉴定
2013/09/19 职场文书
大学系主任推荐信范文
2013/12/24 职场文书
3.15国际消费者权益日主题活动活动总结
2014/03/16 职场文书
毕业论文指导教师评语
2014/12/30 职场文书
哈姆雷特读书笔记
2015/06/29 职场文书
Python数据结构之队列详解
2022/03/21 Python