日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

JavaScript 防抖和節(jié)流遇見的奇怪問題及解決

瀏覽:185日期:2023-10-07 15:17:56

場景

網(wǎng)絡(luò)上已經(jīng)存在了大量的有關(guān) 防抖 和 節(jié)流 的文章,為何吾輩還要再寫一篇呢?事實(shí)上,防抖和節(jié)流,吾輩在使用中發(fā)現(xiàn)了一些奇怪的問題,并經(jīng)過了數(shù)次的修改,這里主要分享一下吾輩遇到的問題以及是如何解決的。

為什么要用防抖和節(jié)流?

因?yàn)槟承┖瘮?shù)觸發(fā)/調(diào)用的頻率過快,吾輩需要手動(dòng)去限制其執(zhí)行的頻率。例如常見的監(jiān)聽滾動(dòng)條的事件,如果沒有防抖處理的話,并且,每次函數(shù)執(zhí)行花費(fèi)的時(shí)間超過了觸發(fā)的間隔時(shí)間的話 ? 頁面就會(huì)卡頓。

演進(jìn)

初始實(shí)現(xiàn)

我們先實(shí)現(xiàn)一個(gè)簡單的去抖函數(shù)

function debounce(delay, action) { let tId return function(...args) { if (tId) clearTimeout(tId) tId = setTimeout(() => { action(...args) }, delay) }}

測試一下

// 使用 Promise 簡單封裝 setTimeout,下同const wait = ms => new Promise(resolve => setTimeout(resolve, ms));(async () => { let num = 0 const add = () => ++num add() add() console.log(num) // 2 const fn = debounce(10, add) fn() fn() console.log(num) // 2 await wait(20) console.log(num) // 3})()

好了,看來基本的效果是實(shí)現(xiàn)了的。包裝過的函數(shù) fn 調(diào)用了兩次,卻并沒有立刻執(zhí)行,而是等待時(shí)間間隔過去之后才最終執(zhí)行了一次。

this 怎么辦?

然而,上面的實(shí)現(xiàn)有一個(gè)致命的問題,沒有處理 this!當(dāng)你用在原生的事件處理時(shí)或許還不覺得,然而,當(dāng)你使用了 ES6 class 這類對 this 敏感的代碼時(shí),就一定會(huì)遇到 this 帶來的問題。

例如下面使用 class 來聲明一個(gè)計(jì)數(shù)器

class Counter { constructor() { this.i = 0 } add() { this.i++ }}

我們可能想在 constructor 中添加新的屬性 fn

class Counter { constructor() { this.i = 0 this.fn = debounce(10, this.add) } add() { this.i++ }}

但很遺憾,這里的 this 綁定是有問題的,執(zhí)行以下代碼試試看

const counter = new Counter()counter.fn() // Cannot read property ’i’ of undefined

會(huì)拋出異常 Cannot read property ’i’ of undefined,究其原因就是 this 沒有綁定,我們可以手動(dòng)綁定 this .bind(this)

class Counter { constructor() { this.i = 0 this.fn = debounce(10, this.add.bind(this)) } add() { this.i++ }}

但更好的方式是修改 debounce,使其能夠自動(dòng)綁定 this

function debounce(delay, action) { let tId return function(...args) { if (tId) clearTimeout(tId) tId = setTimeout(() => { action.apply(this, args) }, delay) }}

然后,代碼將如同預(yù)期的運(yùn)行

;(async () => { class Counter { constructor() { this.i = 0 this.fn = debounce(10, this.add) } add() { this.i++ } } const counter = new Counter() counter.add() counter.add() console.log(counter.i) // 2 counter.fn() counter.fn() console.log(counter.i) // 2 await wait(20) console.log(counter.i) // 3})()

返回值呢?

不知道你有沒有發(fā)現(xiàn),現(xiàn)在使用 debounce 包裝的函數(shù)都沒有返回值,是完全只有副作用的函數(shù)。然而,吾輩還是遇到了需要返回值的場景。例如:輸入停止后,使用 Ajax 請求后臺(tái)數(shù)據(jù)判斷是否已存在相同的數(shù)據(jù)。

修改 debounce 成會(huì)緩存上一次執(zhí)行結(jié)果并且有初始結(jié)果參數(shù)的實(shí)現(xiàn)

function debounce(delay, action, init = undefined) { let flag let result = init return function(...args) { if (flag) clearTimeout(flag) flag = setTimeout(() => { result = action.apply(this, args) }, delay) return result }}

調(diào)用代碼變成了

;(async () => { class Counter { constructor() { this.i = 0 this.fn = debounce(10, this.add, 0) } add() { return ++this.i } } const counter = new Counter() console.log(counter.add()) // 1 console.log(counter.add()) // 2 console.log(counter.fn()) // 0 console.log(counter.fn()) // 0 await wait(20) console.log(counter.fn()) // 3})()

看起來很完美?然而,沒有考慮到異步函數(shù)是個(gè)大失敗!

嘗試以下測試代碼

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) const fn = debounce(10, get, 0) fn(3).then(i => console.log(i)) // fn(...).then is not a function fn(4).then(i => console.log(i)) await wait(20) fn(5).then(i => console.log(i))})()

會(huì)拋出異常 fn(...).then is not a function,因?yàn)槲覀儼b過后的函數(shù)是同步的,第一次返回的值并不是 Promise 類型。

除非我們修改默認(rèn)值

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) // 注意,修改默認(rèn)值為 Promise const fn = debounce(10, get, new Promise(resolve => resolve(0))) fn(3).then(i => console.log(i)) // 0 fn(4).then(i => console.log(i)) // 0 await wait(20) fn(5).then(i => console.log(i)) // 4})()

支持有返回值的異步函數(shù)

支持異步有兩種思路

將異步函數(shù)包裝為同步函數(shù) 將包裝后的函數(shù)異步化

第一種思路實(shí)現(xiàn)

function debounce(delay, action, init = undefined) { let flag let result = init return function(...args) { if (flag) clearTimeout(flag) flag = setTimeout(() => { const temp = action.apply(this, args) if (temp instanceof Promise) { temp.then(res => (result = res)) } else { result = temp } }, delay) return result }}

調(diào)用方式和同步函數(shù)完全一樣,當(dāng)然,是支持異步函數(shù)的

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) // 注意,修改默認(rèn)值為 Promise const fn = debounce(10, get, 0) console.log(fn(3)) // 0 console.log(fn(4)) // 0 await wait(20) console.log(fn(5)) // 4})()

第二種思路實(shí)現(xiàn)

const debounce = (delay, action, init = undefined) => { let flag let result = init return function(...args) { return new Promise(resolve => { if (flag) clearTimeout(flag) flag = setTimeout(() => { result = action.apply(this, args) resolve(result) }, delay) setTimeout(() => { resolve(result) }, delay) }) }}

調(diào)用方式支持異步的方式

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) // 注意,修改默認(rèn)值為 Promise const fn = debounce(10, get, 0) fn(3).then(i => console.log(i)) // 0 fn(4).then(i => console.log(i)) // 4 await wait(20) fn(5).then(i => console.log(i)) // 5})()

可以看到,第一種思路帶來的問題是返回值永遠(yuǎn)會(huì)是 舊的 返回值,第二種思路主要問題是將同步函數(shù)也給包裝成了異步。利弊權(quán)衡之下,吾輩覺得第二種思路更加正確一些,畢竟使用場景本身不太可能必須是同步的操作。而且,原本 setTimeout 也是異步的,只是不需要返回值的時(shí)候并未意識(shí)到這點(diǎn)。

避免原函數(shù)信息丟失

后來,有人提出了一個(gè)問題,如果函數(shù)上面攜帶其他信息,例如類似于 jQuery 的 $,既是一個(gè)函數(shù),但也同時(shí)含有其他屬性,如果使用 debounce 就找不到了呀

一開始吾輩立刻想到了復(fù)制函數(shù)上面的所有可遍歷屬性,然后想起了 ES6 的 Proxy 特性 ? 這實(shí)在是太魔法了。使用 Proxy 解決這個(gè)問題將異常的簡單 ? 因?yàn)槌苏{(diào)用函數(shù),其他的一切操作仍然指向原函數(shù)!

const debounce = (delay, action, init = undefined) => { let flag let result = init return new Proxy(action, { apply(target, thisArg, args) { return new Promise(resolve => { if (flag) clearTimeout(flag) flag = setTimeout(() => { resolve((result = Reflect.apply(target, thisArg, args))) }, delay) setTimeout(() => { resolve(result) }, delay) }) }, })}

測試一下

;(async () => { const get = async i => i get.rx = ’rx’ console.log(get.rx) // rx const fn = debounce(10, get, 0) console.log(fn.rx) // rx})()

實(shí)現(xiàn)節(jié)流

以這種思路實(shí)現(xiàn)一個(gè)節(jié)流函數(shù) throttle

/** * 函數(shù)節(jié)流 * 節(jié)流 (throttle) 讓一個(gè)函數(shù)不要執(zhí)行的太頻繁,減少執(zhí)行過快的調(diào)用,叫節(jié)流 * 類似于上面而又不同于上面的函數(shù)去抖, 包裝后函數(shù)在上一次操作執(zhí)行過去了最小間隔時(shí)間后會(huì)直接執(zhí)行, 否則會(huì)忽略該次操作 * 與上面函數(shù)去抖的明顯區(qū)別在連續(xù)操作時(shí)會(huì)按照最小間隔時(shí)間循環(huán)執(zhí)行操作, 而非僅執(zhí)行最后一次操作 * 注: 該函數(shù)第一次調(diào)用一定會(huì)執(zhí)行,不需要擔(dān)心第一次拿不到緩存值,后面的連續(xù)調(diào)用都會(huì)拿到上一次的緩存值 * 注: 返回函數(shù)結(jié)果的高階函數(shù)需要使用 {@link Proxy} 實(shí)現(xiàn),以避免原函數(shù)原型鏈上的信息丟失 * * @param {Number} delay 最小間隔時(shí)間,單位為 ms * @param {Function} action 真正需要執(zhí)行的操作 * @return {Function} 包裝后有節(jié)流功能的函數(shù)。該函數(shù)是異步的,與需要包裝的函數(shù) {@link action} 是否異步?jīng)]有太大關(guān)聯(lián) */const throttle = (delay, action) => { let last = 0 let result return new Proxy(action, { apply(target, thisArg, args) { return new Promise(resolve => { const curr = Date.now() if (curr - last > delay) { result = Reflect.apply(target, thisArg, args) last = curr resolve(result) return } resolve(result) }) }, })}

總結(jié)

嘛,實(shí)際上這里的防抖和節(jié)流仍然是簡單的實(shí)現(xiàn),其他的像 取消防抖/強(qiáng)制刷新緩存 等功能尚未實(shí)現(xiàn)。當(dāng)然,對于吾輩而言功能已然足夠了,也被放到了公共的函數(shù)庫 rx-util 中。

以上就是JavaScript 防抖和節(jié)流遇見的奇怪問題及解決的詳細(xì)內(nèi)容,更多關(guān)于JavaScript 防抖和節(jié)流的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: JavaScript
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
黑森林国产精品av| 蜜臀国产一区| 777久久精品| 麻豆精品久久久| 欧美天堂视频| 亚洲在线免费| 国产欧美自拍| 成人污污视频| 99国产精品久久久久久久| 免费在线视频一区| 久久激五月天综合精品| 中文字幕高清在线播放| 亚洲欧美成人综合| 国产日韩中文在线中文字幕| 岛国av在线网站| 国产精品美女| 国产精品任我爽爆在线播放| 色爱综合av| 午夜精品影视国产一区在线麻豆| 精品三级在线观看视频| 欧美特黄视频| 国产精品极品| 影音先锋国产精品| 国产精品亚洲综合色区韩国 | 蜜桃精品在线| 色综合视频一区二区三区日韩 | 久久九九精品| 国产精品videossex| 久久99伊人| 色天使综合视频| 老色鬼精品视频在线观看播放| 亚洲经典在线| 国产在线观看www| 久久不卡日韩美女| 亚洲精品无播放器在线播放| 久久青草久久| av资源新版天堂在线| 国产探花一区| 热久久国产精品| 欧美1区免费| 欧美aa一级| 精品成av人一区二区三区| 日韩精品亚洲aⅴ在线影院| 国产视频久久| 欧美日韩一二三四| 另类中文字幕国产精品| 美女久久久精品| 日本91福利区| 综合干狼人综合首页| 国产麻豆综合| 黄色在线一区| 亚洲激情av| 亚洲激精日韩激精欧美精品| 国产一区欧美| 精品一区欧美| 婷婷中文字幕一区| 久久高清免费| 日本久久成人网| 亚洲1234区| 日韩一区三区| 久久久久国产一区二区| 欧美好骚综合网| 麻豆精品少妇| 麻豆一区二区在线| 精品久久中文| 成人精品视频| 日韩精品中文字幕第1页| 日韩免费视频| 亚洲二区视频| 91久久中文| 99国内精品| 亚洲综合精品四区| 免费不卡在线视频| 日韩综合一区二区| 国产区精品区| 久久精品国产999大香线蕉| 国产精品99久久免费观看| 国产精品三p一区二区| 欧美国产日本| 大香伊人久久精品一区二区| 欧美激情另类| 99视频精品全部免费在线视频| 激情视频一区二区三区| 亚洲经典在线| 免费在线观看日韩欧美| 在线精品观看| 欧美一级一区| 久久69成人| 日韩欧美自拍| 99香蕉国产精品偷在线观看| 蜜臀精品久久久久久蜜臀| 国产亚洲网站| 另类专区亚洲| 国产麻豆一区二区三区| 国产精品密蕾丝视频下载| 国产另类在线| 麻豆精品蜜桃视频网站| 麻豆视频观看网址久久| 精品中国亚洲| 久久久久久色| 国产精品一级在线观看| 国产精品任我爽爆在线播放| 久久精品97| 麻豆免费精品视频| 97精品国产| 色婷婷久久久| 亚洲精品电影| 亚洲精品乱码久久久久久蜜桃麻豆| 国产色噜噜噜91在线精品| 日本一区二区免费高清| 久久人人99| 亚洲精品免费观看| 久久精品免视看国产成人| 久久婷婷亚洲| 偷拍亚洲精品| 国产精品毛片一区二区在线看| 亚洲午夜在线| 日韩精品欧美大片| 国产精品99久久精品| 国产美女一区| 美女久久久精品| 欧美/亚洲一区| 日韩精选在线| 麻豆精品蜜桃| 青青国产精品| 99视频精品全部免费在线视频| 亚洲精品中文字幕99999| 国产成人精品一区二区三区在线| 国产一区二区高清| 久久精品毛片| 免费人成黄页网站在线一区二区 | 国产一区二区视频在线看| 欧美日韩四区| 麻豆精品99| 国产精品色网| 国产精品网在线观看| 亚洲一级影院| 国产精品一页| 尤物精品在线| 精品免费在线| 中文字幕一区二区三区四区久久 | 99久久夜色精品国产亚洲1000部| 日韩国产一二三区| 亚洲成人二区| 国产精品xxxav免费视频| 性欧美xxxx免费岛国不卡电影| 日韩国产在线观看| 91精品电影| 精品精品国产三级a∨在线| 视频精品一区二区| 香蕉成人av| 国产精品一区二区免费福利视频| 欧美日韩激情| 老鸭窝一区二区久久精品| 久久国产高清| 亚洲精品一区三区三区在线观看| 欧美日本一区| 爽爽淫人综合网网站| bbw在线视频| 国产欧美久久一区二区三区| 91久久中文| av高清不卡| 国产精品第十页| 三级在线观看一区二区 | 久久亚洲视频| 精品成人免费一区二区在线播放| 国产精品一级| 亚洲精品护士| 狠狠爱成人网| 午夜精品成人av| 精品三级国产| 久久精品97| 日精品一区二区三区| 亚洲经典在线| 日韩欧美网址| 精品国产日韩欧美精品国产欧美日韩一区二区三区 | 国产日韩一区二区三区在线播放| 国产一区二区精品| 久久九九电影| 亚洲欧洲美洲av| 国产精品蜜月aⅴ在线| 亚洲aa在线| 亚洲在线观看| 亚洲精品午夜av福利久久蜜桃| 日产精品一区二区| 精品资源在线| 久久国产66| 99久久久久久中文字幕一区| 九九综合九九| 久久精品99国产国产精| 久久三级福利| 国产精品大片| 婷婷精品在线| 久久99国产精品视频| 狠狠干成人综合网| 久久国产麻豆精品| 激情欧美一区二区三区| 久久的色偷偷| 日韩影片在线观看| 99re国产精品|