概念
- 申明在一个函数中的函数,叫做闭包函数。
- 通常状况下,在Javascript语言中,只有函数外部的子函数能力读取局部变量:
function f1(){ var n = 999;}console.log(n); // n is not defined
- 闭包是一种非凡的作用域:其返回的外部函数的作用域中保留着父级的变量对象和作用域连贯,所以外部函数总是能够拜访其所在的内部函数中申明的参数和变量,即便在其内部函数被销毁之后。
- 所以,在实质上,闭包就是将函数外部和函数内部连接起来的一座桥梁。
特点
- 让内部拜访函数外部变量成为可能
- 局部变量会常驻在内存中
- 能够防止应用全局变量,避免全局变量净化
- 会造成内存透露(有一块内存空间被长期占用,而不被开释)
利用场景实例
防抖debounce
- 构想有此场景:输入框中内容变动须要实时申请接口以获取最新搜寻后果,如果在输出实现前输入框内容每变动一下都去申请接口,会造成很多不必要的申请,大大增加服务器压力。
- 解决思路:有变动时提早一段时间再执行function,若在这段延迟时间内又有新变动,则从新开始提早
// 定时器期间,有新操作时,清空旧定时器,重设新定时器 var debounce = (fn, wait) => { let timer, timeStamp=0; let context, args; let run = ()=>{ timer= setTimeout(()=>{ fn.apply(context,args); },wait); } let clean = () => { clearTimeout(timer); } return function() { context = this; args = arguments; let now = (new Date()).getTime(); if (now-timeStamp < wait) { console.log('reset',now); // 革除定时器,并重新加入提早 clean(); run(); } else { console.log('set',now); run(); // last timer alreay executed, set a new timer } timeStamp = now; } }
- 代码进一步优化:周期内有新事件触发时,重置定时器开始工夫戳,定时器执行时,判断开始工夫戳,若开始工夫戳被推后,从新设定延时定时器;退出是否立刻执行参数。
// 减少前缘触发性能var debounce = (fn, wait, immediate=false) => { let timer, startTimeStamp=0; let context, args; let run = (timerInterval) => { timer= setTimeout(() => { let now = (new Date()).getTime(); let interval = now-startTimeStamp if(interval < timerInterval) { // the timer start time has been reset,so the interval is less than timerInterval console.log('debounce reset',timerInterval-interval); startTimeStamp = now; run(wait-interval); // reset timer for left time } else { if (!immediate) { fn.apply(context,args); } clearTimeout(timer); timer=null; } }, timerInterval); } return function() { context = this; args = arguments; let now = (new Date()).getTime(); startTimeStamp = now; // set timer start time if(!timer) { console.log('debounce set',wait); if(immediate) { fn.apply(context,args); } run(wait); // last timer alreay executed, set a new timer } }}
节流throttling
- 构想有此场景:有‘搜寻’按钮,每点击一次都会从新申请接口,获取并渲染页面表格最新数据,如果短时间内间断点击按钮,仍然会造成很多不必要的申请
- 解决思路:在一段时间内只执行最初一次function
// 定时器期间,只执行最初一次操作var throttling = (fn, wait) => { let timer; let context, args; let run = () => { timer=setTimeout(()=>{ fn.apply(context,args); clearTimeout(timer); timer=null; },wait); } return function () { context=this; args=arguments; if(!timer){ console.log("throttle, set"); run(); }else{ console.log("throttle, ignore"); } }}
// 减少前缘var throttling = (fn, wait, immediate) => { let timer, timeStamp=0; let context, args; let run = () => { timer=setTimeout(()=>{ if(!immediate){ fn.apply(context,args); } clearTimeout(timer); timer=null; },wait); } return function () { context=this; args=arguments; if(!timer){ console.log("throttle, set"); if(immediate){ fn.apply(context,args); } run(); }else{ console.log("throttle, ignore"); } }}