防抖 操作時(shí)不執(zhí)行 確定不操作了才執(zhí)行莫换,在前端應(yīng)用場(chǎng)景還是比較多的
好比你在一直玩手機(jī)的時(shí)候,手機(jī)不會(huì)息屏 玩了一分鐘不玩了 就息屏了
原理: 操作(你快動(dòng)我爸枇濉)=>關(guān)閉定時(shí)器(好了拉岁,我要重新數(shù)數(shù)了)=>重新開始計(jì)算器(你再不動(dòng)我就要息屏了)=>執(zhí)行(手機(jī)息屏了)
如何去實(shí)現(xiàn)這么一個(gè)函數(shù)
1.創(chuàng)建一個(gè)函數(shù),他接受倆個(gè)參數(shù)惰爬,一個(gè)是你想去防抖的函數(shù)喊暖,一個(gè)是你想延時(shí)操作的時(shí)間
function debounce(fun,delay){
}
2.他返回一個(gè)自執(zhí)行函數(shù),并且這個(gè)函數(shù)在 delay后執(zhí)行
function debounce(fun,delay){
return function(){
setTimeout(()=>{
fun()
},delay)
}
}
此時(shí)其實(shí)已經(jīng)實(shí)現(xiàn)了一個(gè)延時(shí)函數(shù)了撕瞧,但是需要自執(zhí)行一下陵叽,因?yàn)槲覀兎祷氐氖呛瘮?shù)體狞尔,可以使用debounce(fun,delay)(),或者return function(){}()加個(gè)括弧
function debounce(fun,delay){
return function(){
setTimeout(()=>{
fun()
},delay)
}
}
function sleep(){
console.log(1)
}
debounce(sleep,3000)()
那當(dāng)然這種代碼肯定沒有如下來的簡(jiǎn)潔
const sleep = time =>{
return new Promise(resolve=>setTimeout(resolve,time))
}
3.再來看一遍原理 操作=>關(guān)閉定時(shí)器=>重新開始計(jì)算器=>執(zhí)行
,那么接下來就是對(duì)定時(shí)器的操作巩掺,我們聲明一個(gè)定時(shí)器然后并覆蓋
function debounce(fun,delay){
let timer
return function(){
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fun()
},delay)
}
}
那么到這步沪么,其實(shí)已經(jīng)實(shí)現(xiàn)了大多數(shù)的防抖,我們嘗試一下
function watchResize() {
console.log(1)
}
window.addEventListener('resize', debounces(watchResize, 1000))
在頁(yè)面中進(jìn)行窗口改變的時(shí)候锌半,當(dāng)你停止改變1s后就會(huì)打印1禽车,當(dāng)然這個(gè)函數(shù)還有一些優(yōu)化,比如對(duì)fn的類型判斷刊殉,delay的處理等等殉摔,了解基礎(chǔ)原理之后 稍微進(jìn)階一下
<input type="text" id="ipt">
比如我們寫個(gè)input,給它綁定一個(gè)input事件
ipt.oninput = function(){
console.log(this.value)
}
OK记焊,此時(shí)沒有問題逸月,但是我們想要用戶最后輸入的值,做個(gè)防抖
ipt.oninput = debounce(function () {
console.log(this.value)
}, 2000)
此時(shí)控制臺(tái)你會(huì)發(fā)現(xiàn)遍膜,打印的是undefined碗硬,問題就出在this指向問題
ES5: 一個(gè)函數(shù)在被調(diào)用時(shí),會(huì)自動(dòng)取得兩個(gè)特殊變量:this和arguments瓢颅。在全局環(huán)境調(diào)用函數(shù)時(shí)恩尾,this代表window對(duì)象;而當(dāng)函數(shù)被作為其他某個(gè)對(duì)象的方法而調(diào)用時(shí)挽懦,this就代表那個(gè)對(duì)象翰意。
簡(jiǎn)而言之: ES5的函數(shù)中,this是執(zhí)行時(shí)函數(shù)所在的那個(gè)對(duì)象信柿。
function debounce(fun, delay) {
console.log(this) //此處打印的是windows
let timer
return function () {
//此處是執(zhí)行環(huán)境 因?yàn)榉祷氐暮瘮?shù) 在xx環(huán)境下觸發(fā)的 xx就是調(diào)用的對(duì)象
console.log(this)
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fun()
}, delay)
}
}
所以我們?cè)诙〞r(shí)器的函數(shù)執(zhí)行顯示綁定當(dāng)前的this即可
function debounce(fun, delay) {
let timer
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fun.apply(this)
}, delay)
}
}
同理冀偶,調(diào)用的時(shí)候就不能使用箭頭函數(shù),箭頭函數(shù)不存在this渔嚷,當(dāng)然你非要用 进鸠,可以這樣用
const co = (obj)=>obj.value
ipt.oninput = debounce(function(){
console.log(co(this))
},1000)
感覺就挺憨的!形病!
節(jié)流客年,原理差不多,都是操控時(shí)間
好比你打游戲的技能冷卻窒朋,亦或是你等的公交車搀罢,總是多少時(shí)間來一趟蝗岖,如何控制時(shí)間有倆種說法侥猩,可以通過當(dāng)前時(shí)間減去上一次時(shí)間,也可以通過設(shè)置的時(shí)間去操作抵赢,兩種思路都可以
我們先按照防抖的思路寫一個(gè)setTimeout版本欺劳,老樣子 先返回一個(gè)函數(shù)唧取,有了前面的基礎(chǔ),現(xiàn)在節(jié)奏快點(diǎn)
function throttle(fn,delay){
return function(){
setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
然后我們?nèi)プ鲆幌驴刂?/p>
function throttle(fn, delay) {
//開啟一個(gè)值 默認(rèn)關(guān)閉
let timer = false
return function () {
//如果這個(gè)timer是開啟的 那么說明我們?cè)跁r(shí)間范圍之外 那我們不做啥 直接返回
if (timer) {
return
}
else {
//開啟這個(gè)值
timer = true
setTimeout(() => {
fn.apply(this, arguments)
//說明下一次可以執(zhí)行了 那你就繼續(xù)false吧
timer = false
}, delay)
}
}
}
window.addEventListener('resize', throttle(watchresize, 2000))
然后你發(fā)現(xiàn)在一直操作窗口的時(shí)候划提,2s觸發(fā)一次咯
function throttle(fun, delay) {
let previous = 0;
return function () {
let now = Date.now();
let args = arguments;
if (now - previous > delay) {
func.apply(this, args);
previous = now;
}
}
}
也可以使用時(shí)間戳去控制7愕堋! 看完嘗試自己再手寫幾遍就應(yīng)該可以理解了