函数主宰任何编程语言。在js中,函数是另类的存在,本质上是一个特殊的Object,可以设置属性:constfn=()=>{};fn.foo="foo";console.log(fn.foo);//'foo'今天分享了函数的一些操作:functionbufferfunctionmemoizefunctioncurrycurryinterceptparameterprocessingarg防抖节流delayfunctionexecutiondelaydelayfunctioncalldeferasynchronousfunctioncallcomposefunctioniscalledonlyonceonce判断函数是否可以执行Check对象属性checkProp链调用函数memoize的缓冲函数对memoize的思考来源于reack的Hook文档。memoize的特点是“利用函数的特性进行缓存”。不知道大家在做算法的时候有没有考虑过递归是如何缓存结果并逐层存储的。对于下面的Fibonacci,每次计算的结果缓存在哪里?constfibonacci=(n)=>{return<2?n:fibonacci(n-1)+fibonacci(n-2);};我们可以简单的模拟memoize的实现:constmemoize=function(fn){constcache={};returnfunction(){constkey=JSON.stringify(arguments);varvalue=cache[key];if(!value){//Join为了了解进程,正式场合应该去掉日志。console.log('newvalue,executing...');//放在数组中,方便undefined,null等异常情况value=[fn.apply(this,arguments)];cache[key]=value;}else{console.log('fromcache');}returnvalue[0];}}测试它:constmemoizeFibonacci=memoize(fibonacci);constlog=console.log;log(memoizeFibonacci(45));//新值,正在执行...;1134903170//等待时间比较长log(memoizeFibonacci(45));//来自缓存;1134903170log(memoizeFibonacci(45));//来自缓存;1134903170log(memoizeFibonacci(45));//从缓存中获取;1134903170log(memoizeFibonacci(45));函数柯里化柯里化的概念是“将接受多个参数的函数转换为接受单个参数的函数”。constcurry=(fn,arity=fn.length,...args)=>arity<=args.length?fn(...args):curry.bind(null,fn,arity,...args);咖喱(Math.pow)(2)(10);//1024curry(Math.min,3)(10)(50)(2);//2这个bind用的很好,用它积累传入的Parameters,等到参数足够了,再调用。有了柯里化,也有反柯里化。它的概念是“扁平化接受多个参数的多个函数”。constuncurry=(fn,n=1)=>(...args)=>{constnext=acc=>args=>args.reduce((x,y)=>x(y),acc);if(n>args.length)thrownewRangeError('Argumentstoofew!');returnext(fn)(args.slice(0,n));};constadd=x=>y=>z=>x+y+z;constuncurredAdd=uncurry(add,3);uncurredAdd(1,2,3);//6拦截函数参数ary"拦截指定函数参数进行操作";ary的第二个参数接收一个index参数,表示只截取第n个位置。//ary截取指定参数处理constary=(fn,n)=>(args)=>fn(args.slice(0,n));//如果处理的数据是字符串constcheckPe=(arg)=>{if(arg&&arg.indexOf('pe')>-1){returnarg.indexOf('pe')}return-1}constgetPe=ary(checkPe,5);constnumsPe=['wpe','wwperr','wwepe'].map(x=>getPe(x));console.log(numsPe,'numsPe')//[1,2,3]如果是数组,需要使用展开运算符。//如果处理后的数据是数组constary=(fn,n)=>(...args)=>fn(...args.slice(0,n));constfirstTwoMax=ary(Math.max,3);constnums=[[2,6,9,'a'],[6,4,8],[10]].map(x=>firstTwoMax(...x));console.log(nums,'nums')//[9,8,10]防抖节流防抖和节流的区别可以参考我之前的文章《电梯与地铁之说》。constdebounce=(fn,ms=0)=>{lettimeoutId;returnfunction(...args){clearTimeout(timeoutId);timeoutId=setTimeout(()=>fn.apply(this,args),ms);};};window.addEventListener('resize',debounce(()=>{console.log(window.innerWidth);console.log(window.innerHeight);},250)传入经常调用的函数和时间间隔,返回一个debouncedfunction.Throttling会稀释函数的执行频率.wait秒内只执行一次.constthrottle=(fn,wait)=>{letinThrottle,lastFn,lastTime;returnfunction(){constcontext=this,args=arguments;if(!inThrottle){fn.apply(context,args);lastTime=Date.now();inThrottle=true;}else{clearTimeout(lastFn);lastFn=setTimeout(function(){if(Date.now()-lastTime>=wait){fn.apply(context,args);lastTime=Date.now();}},Math.max(wait-(Date.now()-lastTime),0));}};};window.addEventListener('resize',throttle(function(evt){console.log(window.innerWidth);console.log(window.innerHeight);},250));//将logthewindowdimensionsatmostevery250ms延迟函数执行ondelaydelay字面意思是:“延迟执行”。constdelay=(fn,wait,...args)=>setTimeout(fn,wait,...args);delay(function(text){console.log(text);},1000,'later');//一秒后记录“稍后”。延迟函数调用deferdefer字面意思是:“延迟调用”。可用于推迟cpu密集型计算,以免阻塞渲染引擎工作。使用setTimeout(超时为1ms)将函数参数添加到浏览器事件队列的末尾。constdefer=(fn,...args)=>setTimeout(fn,1,...args);//例子A:defer(console.log,'a'),console.log('b');//logs'b'then'a'异步函数composecompose函数是“从右到左实现的数据执行流程”。它的真正意义在于逻辑层次。使用reduce方法实现函数的“洋葱”包装。constcompose=(...fns)=>fns.reduce((f,g)=>(...args)=>f(g(...args)));constsubstract3=x=>x-3;constadd5=x=>x+5;constmultiply=(x,y)=>x*y;constmultiplyAndAdd5AndSubstract3=compose(substract3,add5,multiply);multiplyAndAdd5AndSubstract3(5,2);//12想从左到右实现执行也很简单,调整f和g的位置即可。该函数只调用一次,因为JavaScript是单线程执行环境,不需要考虑并发环境。闭包中直接存放了一个内部变量,每次调用前进行判断,第一次调用时修改其值,使得后续所有调用都失败。constonce=(fn)=>{letcalled=false;returnfunction(...args){if(called)return;called=true;returnfn.apply(this,args);};};conststartApp=function(event){console.log(this,event);//document.body,MouseEvent};document.body.addEventListener("click",once(startApp));判断函数是否可以执行第一个参数是判断函数是否可以执行的条件,第二个参数是要执行的函数。constwhen=(pred,whenTrue)=>(x)=>(pred(x)?whenTrue(x):x);constdoubleEvenNumbers=when((x)=>x%2===0,(x)=>x*2);doubleEvenNumbers(2);//4doubleEvenNumbers(1);//1检查对象属性“判断一个对象是否符合要求”。使用!!强制转换为布尔类型。constcheckProp=(predicate,prop)=>(obj)=>!!predicate(obj[prop]);constlengthIs4=checkProp((l)=>l===4,"length");lengthIs4([]);//falselengthIs4([1,2,3,4]);//trueconstsizeIs4=checkProp((l)=>l===4,"size");sizeIs4(newSet([1,2,3,4]));//trueconstsession={obj:{active:true,disabled:false}};constvalidUserSession=checkProp((u)=>u.active&&!u.disabled,"obj");validUserSession(session);//trueChaining将函数数组转换为具有决策权的链式函数调用。constchainAsync=(fns)=>{letcurr=0;constlast=fns[fns.length-1];constnext=()=>{constfn=fns[curr++];fn===last?fn():fn(next);};next();};chainAsync([(next)=>{console.log("0seconds");setTimeout(next,1000);},(next)=>{console.log("1second");setTimeout(next,1000);},()=>{console.log("2second");},]);本文转载自微信公众号「惊码盗版」,可关注下方二维码。转载本文请联系惊码盗公众号。
