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

您的位置:首頁技術文章
文章詳情頁

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

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

場景

網絡上已經存在了大量的有關 防抖 和 節流 的文章,為何吾輩還要再寫一篇呢?事實上,防抖和節流,吾輩在使用中發現了一些奇怪的問題,并經過了數次的修改,這里主要分享一下吾輩遇到的問題以及是如何解決的。

為什么要用防抖和節流?

因為某些函數觸發/調用的頻率過快,吾輩需要手動去限制其執行的頻率。例如常見的監聽滾動條的事件,如果沒有防抖處理的話,并且,每次函數執行花費的時間超過了觸發的間隔時間的話 ? 頁面就會卡頓。

演進

初始實現

我們先實現一個簡單的去抖函數

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})()

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

this 怎么辦?

然而,上面的實現有一個致命的問題,沒有處理 this!當你用在原生的事件處理時或許還不覺得,然而,當你使用了 ES6 class 這類對 this 敏感的代碼時,就一定會遇到 this 帶來的問題。

例如下面使用 class 來聲明一個計數器

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 綁定是有問題的,執行以下代碼試試看

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

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

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

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

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

然后,代碼將如同預期的運行

;(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})()

返回值呢?

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

修改 debounce 成會緩存上一次執行結果并且有初始結果參數的實現

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 }}

調用代碼變成了

;(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})()

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

嘗試以下測試代碼

;(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))})()

會拋出異常 fn(...).then is not a function,因為我們包裝過后的函數是同步的,第一次返回的值并不是 Promise 類型。

除非我們修改默認值

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) // 注意,修改默認值為 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})()

支持有返回值的異步函數

支持異步有兩種思路

將異步函數包裝為同步函數 將包裝后的函數異步化

第一種思路實現

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 }}

調用方式和同步函數完全一樣,當然,是支持異步函數的

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) // 注意,修改默認值為 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})()

第二種思路實現

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) }) }}

調用方式支持異步的方式

;(async () => { const get = async i => i console.log(await get(1)) console.log(await get(2)) // 注意,修改默認值為 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})()

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

避免原函數信息丟失

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

一開始吾輩立刻想到了復制函數上面的所有可遍歷屬性,然后想起了 ES6 的 Proxy 特性 ? 這實在是太魔法了。使用 Proxy 解決這個問題將異常的簡單 ? 因為除了調用函數,其他的一切操作仍然指向原函數!

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})()

實現節流

以這種思路實現一個節流函數 throttle

/** * 函數節流 * 節流 (throttle) 讓一個函數不要執行的太頻繁,減少執行過快的調用,叫節流 * 類似于上面而又不同于上面的函數去抖, 包裝后函數在上一次操作執行過去了最小間隔時間后會直接執行, 否則會忽略該次操作 * 與上面函數去抖的明顯區別在連續操作時會按照最小間隔時間循環執行操作, 而非僅執行最后一次操作 * 注: 該函數第一次調用一定會執行,不需要擔心第一次拿不到緩存值,后面的連續調用都會拿到上一次的緩存值 * 注: 返回函數結果的高階函數需要使用 {@link Proxy} 實現,以避免原函數原型鏈上的信息丟失 * * @param {Number} delay 最小間隔時間,單位為 ms * @param {Function} action 真正需要執行的操作 * @return {Function} 包裝后有節流功能的函數。該函數是異步的,與需要包裝的函數 {@link action} 是否異步沒有太大關聯 */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) }) }, })}

總結

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

以上就是JavaScript 防抖和節流遇見的奇怪問題及解決的詳細內容,更多關于JavaScript 防抖和節流的資料請關注好吧啦網其它相關文章!

標簽: JavaScript
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
婷婷成人综合| 精品中文字幕一区二区三区av| 精品国产黄a∨片高清在线| 日韩一区二区三区精品视频第3页 日韩一区二区三区免费视频 | 免费欧美日韩| 亚洲欧美日韩精品一区二区 | 久久最新视频| 一区二区三区四区在线观看国产日韩| 亚洲激情黄色| 噜噜噜躁狠狠躁狠狠精品视频 | 国产一区二区三区不卡视频网站| 久久久久观看| 成年男女免费视频网站不卡| 日韩不卡一区| 精品亚洲美女网站| 国产综合精品| 巨乳诱惑日韩免费av| 亚洲另类视频| 欧美日本不卡| 精品国产乱码久久久| 三级精品视频| 日韩天堂av| 中文字幕成人| 一区二区国产在线观看| 日韩高清在线一区| 麻豆传媒一区二区三区| 波多视频一区| 欧美精品一卡| 欧美一区不卡| 日韩精品电影| 亚洲欧美日韩精品一区二区| 日韩精品91亚洲二区在线观看| 69堂免费精品视频在线播放| 精品久久国产一区| 久久高清免费| 亚洲精品无吗| 久久伊人国产| 国产色综合网| 日韩一区二区三区四区五区| 久久精品一区二区国产| 欧美中文字幕一区二区| 国产中文在线播放| 亚洲一区二区免费看| 久久精品97| 久久精品99久久无色码中文字幕| 综合激情一区| 黄色在线观看www| 香蕉人人精品| 久久激情五月婷婷| 1000部精品久久久久久久久| 日本91福利区| 国产99精品| 欧美日韩xxxx| 久久三级福利| 久久狠狠久久| 亚洲在线网站| 成人午夜网址| 亚洲精选成人| 色婷婷狠狠五月综合天色拍| 日韩精品中文字幕吗一区二区| 国产成人免费视频网站视频社区| 国产美女一区| 久久精品三级| 亚洲欧美日韩精品一区二区| 精品伊人久久| 亚洲丝袜美腿一区| 日韩成人综合| 欧美精品影院| 亚洲一级网站| 国语精品一区| 日本不卡高清视频| 黑丝一区二区| 四虎国产精品免费观看| 91精品丝袜国产高跟在线| 欧美一区三区| 韩日一区二区| 欧美一区影院| 一区在线观看| 国产一区二区精品久| 日韩高清成人在线| 亚洲激情黄色| 国产欧美一区二区三区精品酒店| 婷婷精品在线| 欧美亚洲在线日韩| 国产成人a视频高清在线观看| 亚洲色图国产| 欧美成a人免费观看久久| 国产精品v日韩精品v欧美精品网站| 中文日韩在线| 日韩啪啪电影网| 久久中文字幕导航| 国产欧美日韩精品一区二区免费| 久久亚洲风情| 久久精品主播| 亚洲永久av| 麻豆一区二区在线| 四虎精品一区二区免费| 国产精品毛片在线看| 国产一区二区三区四区大秀| 国产伦一区二区三区| 综合国产精品| 亚洲一区黄色| 亚洲天堂黄色| 97精品视频在线看| 国产福利91精品一区二区| 欧美国产先锋| 国产欧美日韩在线一区二区 | 日韩午夜av在线| 激情婷婷亚洲| 国产黄大片在线观看| 久久狠狠久久| 午夜电影一区| 久久都是精品| 亚洲激情二区| 欧美日韩国产一区精品一区| 久久久久美女| 99久久夜色精品国产亚洲狼 | 国产色播av在线| 手机在线电影一区| 国产福利电影在线播放| 国产精品黑丝在线播放| 精品国产乱码久久久| 欧美激情视频一区二区三区在线播放| 青草综合视频| 国产免费av一区二区三区| 国产亚洲观看| 欧美国产另类| 四虎成人av| 99久久夜色精品国产亚洲狼 | 亚洲精品麻豆| 婷婷成人av| 欧美一级久久| 精品中文字幕一区二区三区四区| 久久精品一区二区国产| 给我免费播放日韩视频| 精品不卡一区| 亚洲精品进入| 国产精品最新| 精品免费av| 日韩在线综合| 黑丝一区二区| 日本在线不卡视频| 国产精品igao视频网网址不卡日韩| 亚欧洲精品视频在线观看| 欧美日韩1区| 精品久久中文| 日韩久久一区二区三区| 91久久久精品国产| 男人的天堂久久精品| 日本在线成人| 国产精品a久久久久| 国产videos久久| 婷婷国产精品| 亚洲69av| 国产一区二区三区久久| 蜜臀av免费一区二区三区| 亚洲一区日韩在线| 欧美日韩亚洲三区| 欧美男人天堂| 日韩中文字幕不卡| 久久国产精品美女| 久久视频精品| 一区二区精品| 国际精品欧美精品| 欧美大黑bbbbbbbbb在线| 免费久久99精品国产自在现线| 日韩一区二区三免费高清在线观看 | 99成人在线| 欧美在线日韩| 亚洲最新无码中文字幕久久| 亚洲一区二区三区高清| 你懂的国产精品永久在线| 欧美sss在线视频| 亚洲精品成a人ⅴ香蕉片| 久久精品欧洲| 一区福利视频| 麻豆91精品视频| 欧美特黄一区| 美女久久久精品| av不卡在线| 国产精成人品2018| 午夜久久福利| 老色鬼精品视频在线观看播放| 999精品一区| 日韩精品国产精品| 四虎影视精品| 国产欧美日韩一区二区三区四区| 国产亚洲一区二区手机在线观看| 石原莉奈一区二区三区在线观看| 精品视频网站| 日韩在线播放一区二区| 免费一级欧美在线观看视频| 午夜欧美理论片| 精品不卡一区| 综合视频一区| 欧美日韩尤物久久| 你懂的网址国产 欧美| 视频国产精品| 久久久久蜜桃| 激情不卡一区二区三区视频在线|