详解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实现无刷新DropDownList联动实现代码
Mar 08 Javascript
jQuery中:input选择器用法实例
Jan 03 Javascript
JavaScript使用shift方法移除素组第一个元素实例分析
Apr 06 Javascript
一个非常好用的文字滚动的案例,鼠标悬浮可暂停[两种方案任选]
Dec 01 Javascript
微信小程序页面开发注意事项整理
May 18 Javascript
ECMAscript 变量作用域总结概括
Aug 18 Javascript
JavaScript实现表单注册、表单验证、运算符功能
Oct 15 Javascript
JavaScript实现的级联算法示例【省市二级联动功能】
Dec 25 Javascript
使用webpack4编译并压缩ES6代码的方法示例
Apr 24 Javascript
微信小程序定义和调用全局变量globalData的实现
Nov 01 Javascript
Javascript实现秒表计时游戏
May 27 Javascript
JavaScript canvas实现流星特效
May 20 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
收音机史话 - 1960年代前后的DIY
2021/03/02 无线电
PHP 5.3.0 安装分析心得
2009/08/07 PHP
PHP 万年历实现代码
2012/10/18 PHP
Zend的MVC机制使用分析(一)
2013/05/02 PHP
php获取qq用户昵称和在线状态(实例分析)
2013/10/27 PHP
ThinkPHP模板IF标签用法详解
2014/07/01 PHP
ThinkPHP模板标签eq if 中区分0,null,false的方法
2017/03/24 PHP
php使用event扩展的io复用测试的示例
2020/10/20 PHP
枚举JavaScript对象的函数
2006/12/22 Javascript
验证用户是否修改过页面的数据的实现方法
2008/09/26 Javascript
js prototype截取字符串函数
2010/04/01 Javascript
Prototype源码浅析 String部分(三)之HTML字符串处理
2012/01/15 Javascript
js动态给table添加/删除tr的方法
2013/08/02 Javascript
JS批量操作CSS属性详细解析
2013/12/16 Javascript
Bootstrap自动适应PC、平板、手机的Bootstrap栅格系统
2016/05/27 Javascript
AngularJs学习第八篇 过滤器filter创建
2016/06/08 Javascript
原生js实现返回顶部缓冲效果
2017/01/18 Javascript
js指定步长实现单方向匀速运动
2017/07/17 Javascript
vue 导航守卫和axios拦截器有哪些区别
2020/12/19 Vue.js
深入浅析Python中join 和 split详解(推荐)
2016/06/30 Python
Python实现将文本生成二维码的方法示例
2017/07/18 Python
python re模块的高级用法详解
2018/06/06 Python
通过Python模块filecmp 对文件比较的实现方法
2018/06/29 Python
python中使用while循环的实例
2019/08/05 Python
python求绝对值的三种方法小结
2019/12/04 Python
TENSORFLOW变量作用域(VARIABLE SCOPE)
2020/01/10 Python
python interpolate插值实例
2020/07/06 Python
python入门教程之基本算术运算符
2020/11/13 Python
css3 实现元素弧线运动的示例代码
2020/04/24 HTML / CSS
Dr. Martens马汀博士德国官网:马丁靴鼻祖
2019/12/26 全球购物
澳大利亚领先的男装零售连锁店:Lowes
2020/08/07 全球购物
社区志愿者心得体会
2014/01/03 职场文书
火锅店创业计划书范文
2014/02/02 职场文书
公司爱心捐款倡议书
2014/05/14 职场文书
应届生找工作求职信
2014/06/24 职场文书
Mysql效率优化定位较低sql的两种方式
2021/05/26 MySQL