今天我想分享一下我在近期有關閾值的一些技巧,因為筆者是一名前端開發,因此下面的demo筆者使用js編寫,但是這篇文章中的技巧是一種思想並不限於js。
我們在寫程式的時候經常會發現這樣一種情況:我們需要根據某一個變數的值屬於哪個區間來控制分支的走向,比如下面的程式碼:
1 2 3 4 5 6 7 |
function changeText(num){ if(num>100){ taskA(); }else{ taskB(); } } |
上面的程式碼邏輯很簡單,當num大於100時,執行taskA函式,反之執行taskB函式(假設taskA、taskB內部實現都有防止連續執行的處理)。對於大多數的場景,這樣的實現是足以滿足判斷的。但是單元測試通過不代表這個模組可以在工程中的任何場景使用。
試想這樣一種情況:我的頁面上有一個DOM元素,taskA()負責將內容修改為“123456”,taskB()負責將內容修改為“654321”。到目前為止上面的程式碼都可以正常執行,但是接下來就是在什麼場合呼叫changeText()了。比如這裡有一個水位監測系統,水位高於100cm時執行任務A(可能是紅燈亮)反之執行任務B(可能是路燈亮),水位剛好為100 左右時,隨著風浪水位不斷在閾值兩邊變化,taskA和taskB即使有防止連續執行的處理,也會交替執行紅燈路燈來回變化不斷的閃爍。兩個任務就會被頻繁改寫。我模擬的是一個抽象的例子,在開發中比如滑鼠移動、滑鼠滾輪、長連線、input事件等等場合都有可能遇到頻繁呼叫+閾值判斷的情形。那麼我們如何防止頻繁經過臨界點,造成的效能損耗和不好的使用者體驗呢。
接下來我將介紹雙閾值的方式。用在這個例子中使水位在100 這個臨界值變動的時候加入方向性,簡單來說從小於100 到超過100的時候綠燈滅、紅燈亮(開始警告);但是水位從高於100 到小於100時,並不馬上紅燈滅、綠燈亮。而是將從上到下的閾值另設定為95(假定風浪造成的資料抖動為5cm),來保證在警報狀態下,水位由高到低變化時,直到水位降到95cm時才解除警報(綠燈亮、紅燈滅)。這樣就在不同的兩個狀態下擁有了各自的閾值。如下圖:
如果用程式碼該怎麼實現呢?既然兩個閾值的使用依賴於當前所處的狀態。那麼我們就要“記錄”狀態,最簡單的方法是由原有的一個引數增加一個狀態引數:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/*雙閾值*/ function doubleTh1(status, num){ var rt = ''; if(status == 'downToUp'){ if(num>100){ rt = taskA(); }else{ rt = taskB(); } }else if(status == 'upToDown'){ if(num <= 95){ rt = taskB(); }else{ rt = taskA(); } } return rt; } |
單元測試:
這只是對雙閾值概念的初級實現,我們不應該每次傳遞狀態值,狀態的記錄應該交給函式自身記錄,每次的狀態依賴上一次返回的結果。我們可以通過閉包來寫一個雙閾值函式的生成器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function doubleTh2(th1, th2, cb1, cb2){ var status = 's1'; return function (num){ var rt = ""; if(status == 's1' && num > th1){ status = 's2'; } if(status == 's2' && num <= th2){ status = 's1'; } if(status == 's1'){ rt = cb2 && cb2() }else if(status == 's2'){ rt = cb1 && cb1(); } return rt; }; } |
呼叫:
1 2 3 4 5 6 7 8 9 10 11 |
var th2 = ts.doubleTh2(100, 95, function (status){ console.log('call taskA'); return 'taskA'; }, function (status){ console.log('call taskB'); return 'taskB'; }); it('值為98時應該執行 taskB', function() { expect(th2(98)).to.be.equal("taskB"); }); |
總結:
這樣我們就完成了一個雙閾值的函式,我們使用這樣的函式來防止位於臨界值的資料抖動。當然感興趣的同學可以擴充套件為多級的判斷函式。