详解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 相关文章推荐
AngularJS语法详解
Jan 23 Javascript
window.open()实现post传递参数
Mar 12 Javascript
JavaScript获取表单内所有元素值的方法
Apr 02 Javascript
JavaScript中定义类的方式详解
Jan 07 Javascript
node.js实现爬虫教程
Aug 25 Javascript
jQuery解析与处理服务器端返回xml格式数据的方法详解
Jul 04 Javascript
javascript实现根据函数名称字符串动态执行函数的方法示例
Dec 28 Javascript
Bootstrap下拉菜单样式
Feb 07 Javascript
ReactNative之键盘Keyboard的弹出与消失示例
Jul 11 Javascript
PHP 实现一种多文件上传的方法
Sep 20 Javascript
100行代码实现一个vue分页组功能
Nov 06 Javascript
原生javascript单例模式的应用实例分析
Feb 23 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 开发工具
2006/12/06 PHP
php5中date()得出的时间为什么不是当前时间的解决方法
2008/06/30 PHP
PHP开发中四种查询返回结果分析
2011/01/02 PHP
php自动加载autoload机制示例分享
2014/02/20 PHP
CodeIgniter框架过滤HTML危险代码
2014/06/12 PHP
PHP生成不重复标识符的方法
2014/11/21 PHP
基于php数组中的索引数组和关联数组详解
2018/03/12 PHP
11款基于Javascript的文件管理器
2009/10/25 Javascript
动态加载外部javascript文件的函数代码分享
2011/07/28 Javascript
[将免费进行到底]在Amazon的一年免费服务器上安装Node.JS, NPM和OurJS博客
2014/08/18 Javascript
jquery实现在页面加载的时自动为日期插件添加当前日期
2014/08/20 Javascript
JavaScript中定义函数的三种方法
2015/03/12 Javascript
理解和运用JavaScript的闭包机制
2015/08/13 Javascript
基于javascript简单实现对身份证校验
2021/01/25 Javascript
jQuery中animate的几种用法与注意事项
2016/12/12 Javascript
原生JS+Canvas实现五子棋游戏实例
2017/06/19 Javascript
js判断输入框不能为空格或null值的实现方法
2018/03/02 Javascript
实例详解BootStrap的动态模态框及静态模态框
2018/08/13 Javascript
JS实现数组深拷贝的方法分析
2019/03/06 Javascript
js实现移动端tab切换时下划线滑动效果
2019/09/08 Javascript
python设置检查点简单实现代码
2014/07/01 Python
Python使用xlrd模块操作Excel数据导入的方法
2015/05/26 Python
在Python 3中实现类型检查器的简单方法
2015/07/03 Python
pandas的object对象转时间对象的方法
2018/04/11 Python
python 求一个列表中所有元素的乘积实例
2019/06/11 Python
python格式化输出保留2位小数的实现方法
2019/07/02 Python
python网络爬虫 CrawlSpider使用详解
2019/09/27 Python
Python 执行矩阵与线性代数运算
2020/08/01 Python
html5构建触屏网站之网站尺寸探讨
2013/01/07 HTML / CSS
canvas中普通动效与粒子动效的实现代码示例
2019/01/03 HTML / CSS
MediaMarkt比利时:欧洲最大电器连锁店
2020/12/21 全球购物
视图的作用
2014/12/19 面试题
C#软件工程师英语面试题
2015/06/07 面试题
自我鉴定思想方面
2013/10/07 职场文书
Vue中foreach数组与js中遍历数组的写法说明
2021/06/05 Vue.js
Android 中的类文件和类加载器详情
2022/06/05 Java/Android