详解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 each函数的链式调用
Jul 22 Javascript
关于IE、Firefox、Opera页面呈现异同 写脚本很痛苦
Aug 28 Javascript
判断目标是否是window,document,和拥有tagName的Element的代码
May 31 Javascript
Javascript数组的排序 sort()方法和reverse()方法
Jun 04 Javascript
基于jquery的可多选的下拉列表框
Jul 20 Javascript
Jquery 获取指定标签的对象及属性的设置与移除
May 29 Javascript
ECMAScript 6即将带给我们新的数组操作方法前瞻
Jan 06 Javascript
js实现鼠标感应图片展示的方法
Feb 27 Javascript
js动态生成form 并用ajax方式提交的实现方法
Sep 09 Javascript
require、backbone等重构手机图片查看器
Nov 17 Javascript
详解jquery和vue对比
Apr 16 jQuery
JavaScript如何把两个数组对象合并过程解析
Oct 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
德生PL330的评价与改造
2021/03/02 无线电
分享php分页的功能模块
2015/06/16 PHP
Zend Framework教程之Zend_Db_Table用法详解
2016/03/21 PHP
PHP中ajax无刷新上传图片与图片下载功能
2017/02/21 PHP
Javascript 作用域使用说明
2009/08/13 Javascript
可以将word转成html的js代码
2010/04/11 Javascript
javascript 从if else 到 switch case 再到抽象
2010/07/17 Javascript
根据对象的某一属性进行排序的js代码(如:name,age)
2010/08/10 Javascript
jquery $.ajax相关用法分享
2012/03/16 Javascript
js控制input框只读实现示例
2014/01/20 Javascript
关闭时刷新父窗口两种方法
2014/05/07 Javascript
移动端JQ插件hammer使用详解
2015/07/03 Javascript
javascript实现table单元格点击展开隐藏效果(实例代码)
2017/04/10 Javascript
js+html5生成自动排列对话框实例
2017/10/09 Javascript
JavaScript中Array方法你该知道的正确打开方法
2018/09/11 Javascript
EasyUI 数据表格datagrid列自适应内容宽度的实现
2019/07/18 Javascript
Python备份Mysql脚本
2008/08/11 Python
举例讲解Python中的Null模式与桥接模式编程
2016/02/02 Python
python中安装Scrapy模块依赖包汇总
2017/07/02 Python
Python基于分析Ajax请求实现抓取今日头条街拍图集功能示例
2018/07/19 Python
在Python中使用gRPC的方法示例
2018/08/08 Python
python如何查看微信消息撤回
2018/11/27 Python
Python3.5迭代器与生成器用法实例分析
2019/04/30 Python
Python with关键字,上下文管理器,@contextmanager文件操作示例
2019/10/17 Python
python 实现多维数组(array)排序
2020/02/28 Python
如何基于windows实现python定时爬虫
2020/05/01 Python
如何理解python中数字列表
2020/05/29 Python
Html5元素及基本语法详解
2016/08/02 HTML / CSS
N.Peal官网:来自伦敦的高档羊绒品牌
2018/10/29 全球购物
金融专业个人求职信范文
2013/11/28 职场文书
《争吵》教学反思
2014/02/15 职场文书
《颐和园》教学反思
2014/02/26 职场文书
2014年财务工作自我评价
2014/09/23 职场文书
判缓刑人员个人思想汇报
2014/10/10 职场文书
2016年“9.22”世界无车日活动小结
2016/04/05 职场文书
SpringBoot全局异常处理方案分享
2022/05/25 Java/Android