js函数只执行一次,函数重写,变量控制与闭包三种做法
一、情景需求
调用后台接口需要附带token信息,那么在每个请求的头部添加token的做法就不太优雅了;一个网站请求100次,那就得写添加100次token,假设某天接口有所变动,改起来就十分麻烦了。
公司项目开发使用的框架是angularjs,正好angularjs有一个请求拦截的功能;意思就是,每次请求的发起,请求拦截都能感知,所以添加token的事我们就可以在请求拦截统一处理。
为了保证请求拦截添加token永远是有效的,我在这里除了添加token,也增加了现有token的过期判断,如果token不存在,或者存在已过期,都得重新拿一次token。
结果骚操作就来,一个页面有10个请求,第一个请求触发token判断并发现已过期,就去请求新的token,巧的是请求token的接口也出了问题,并未能拿回最新token;于是第二个请求也触发了请求拦截,发现token用不了,也去请求新token,同样失败了。
第三次请求拦截失败.....第四次,第五次......第十次,如果这个页面请求更多呢,永无止境了。于是,后台接口成功被我玩炸了。
那么问题来了,请求拦截次数是根据请求触发的,如果token用不了,我去拿新token的操作不管成功失败,其实都只用触发一次,成功了一次搞定,失败了请求100次也都是多余。那么引出我们的问题,如果让js函数只执行一次:
二、实现
1.函数重写
我在js模式第五篇博客有提过函数重写,在这里就是十分适用了:
function fn() { //do something... console.log(1); //函数重写 fn = function () {}; }; fn();//1 fn();//无作为
2.通过变量控制
原理很简单,声明一个默认为true的变量,在执行一次后修改为false,通过条件判断是否需要执行:
let value = true; function fn(){ if(value){ //do something console.log(1); //执行一次后将变量改为false value = false; }; }; fn();//1 fn();//无作为
3.利用闭包
我们可以利用闭包封装一个通用函数,将你需要只执行一次的函数作为参数传入闭包,也可以达到类似的效果:
//封装执行一次通用函数 function once(fn) { if (Object.prototype.toString.call(fn) !== "[object Function]") { throw new Error('请传递一个函数'); }; return function (...rest) { if (fn) { fn.apply(this, rest); fn = null; }; }; }; //我们希望只执行一次的函数 function fn(a, b) { console.log(a + b); }; //调用闭包函数 let onlyOnce = once(fn); onlyOnce(1, 2); //3 onlyOnce(); //无作为
我在网上看到了另外一种写法,原理类似,适用于一次执行的函数有返回值的情况,在第一次调用后,不管再调用几次都将跳过执行过程,直接返回第一次执行的值,也非常实用:
function once(fn) { let can, result; if (Object.prototype.toString.call(fn) !== "[object Function]") { throw new Error('请传递一个函数'); }; return function (...rest) { if (can) { return result; }; can = true; result = fn.apply(this, rest); //释放fn保存的函数,便于被垃圾回收 fn = null; return result; }; }; function fn() { return 1; }; let o = once(fn); o();//1 o();//1
三、小总结
我们在了解很多知识的时候,总会纳闷这个东西的使用场景在哪里,那么阅读完本文,你大概了解了如果让js中函数只执行一次的三种做法,同时也结合了我的需求,对这种用法在何时使用有了一个了解。
我在文章开头提到了angularjs的请求拦截,我发现,vue也同样有这个玩法,要不整一篇请求拦截的文章吧。
那么本文到此结束。
四、参考