假如想统计 JS 中的函数执行次数最多的是哪个, 执行时间最长的是哪个, 该怎么做呢?
1. 统计函数执行次数
2. 统计函数执行时间
3. 如何控制函数的调用次数
4. 如何控制函数的执行时间
一, 统计函数执行次数
常规的方法可以使用 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 机制 http://zxc0328.github.io/2017/09/28/react-16-source/ , 在某种意义上是能控制函数的执行时机, 得空再去看看它是怎么实现的吧
来源: https://www.cnblogs.com/imwtr/p/9582150.html