Lidemy / mentor-program-4th

程式導師實驗計畫第四期課綱
https://bootcamp.lidemy.com
116 stars 60 forks source link

第十六週練習題 #16

Open aszx87410 opened 4 years ago

aszx87410 commented 4 years ago

底下提供十六週相關的小題目讓大家練習,都是以後在工作上可能會碰到的案例

這邊我提供了一個 codesandbox 的環境,你的任務是實作出 Robot 這個 class 以及 debounce 跟 memoize 這兩個 function,裡面有提供相對應的測試檔,測試過了應該就是通過了:https://codesandbox.io/s/closure-homeworks-vh5j3?file=/index.js

至於題目的敘述請參考下面,另外,請勿針對底下那兩個 function 的關鍵字做搜尋,因為你一搜尋了,答案就出現了。如果想確認正確的行為是什麼,可以參考測試檔。

Robot

請你寫出一個 Robot 的 class,初始化的時候可以設置座標 x 跟 y 接著 Robot 會有兩個方法,getCurrentPosition 跟 go,前者會回傳現在機器人所在的 x 與 y 座標,後者可以讓機器人往東南西北任一方向移動,需要傳進 'N', "E", 'S', 'W' 任何一個字串,代表要往哪一個方向走

這個世界是我們所熟悉的二維座標系,因此往北走 Y 座標會增加,往南走 Y 座標會減少,往東走 X 座標會增加,往西走 X 座標會減少

Debounce

在程式的領域中有兩個技巧滿常會用到,分別是 throttle 與 debounce,這邊我就只介紹什麼是 debounce。

舉例來說,假設今天有個 auto complete 的 input,像是 Google 搜尋那樣,在你打字的時候,就會邊發 API request 去後端拿搜尋的建議,然後顯示在前端。

一個簡單的實作會長這樣:

// 每當 input 的內容有變動,就呼叫 handleChange
$('input').change(handleChange)

function handleChange(e) {
  // 拿到 input 的值
  const value = e.target.value

  // 發 api 去後端拿搜尋建議,然後 render 出來
  // 細節我就不寫了
  getAutoSuggestions(value)
}

可是有一個小問題,那就是現在每打一個字就會發一個 request,例如說我搜尋:lidemy,就會發出六個 request,分別是:

  1. l
  2. li
  3. lid
  4. lide
  5. lidem
  6. lidemy

可是這樣是很沒有效率的,因為在我打字的時候,其實真正需要搜尋的結果只有最後的字串 lidemy,中間那些都是可以跳過的。

那解決方法是什麼呢?

有一個解法叫做 debounce,之前看到一個比喻滿傳神的,就是搭公車。司機什麼時候會關車門?確保真的都沒有人要上車以後。而為了確保這件事,就必須等待。

例如說現在第一個人上車了,那司機就等 2 秒後再關門,如果等了 1 秒之後有第二個人上車了,他就再等兩秒。這兩秒間如果又有人上車了,那就再等 2 秒,所以有可能會一直等下去。那如果 2 秒內沒人上車了,司機就可以開車了。

這概念就叫做 debounce,你有一個 threshold(閾值),在這裡面如果再度觸發這個事件,就再繼續等待,反之則做原本要做的事。

以上面 input 的案例來講,通常我們會把 debounce 的 threshold 設在 250ms,意思就是你打完一個字之後,要 250ms 後才會真的去發 api 拿東西,如果你在 250ms 以內又打了新的字,就會再等 250ms。

以正常的使用狀況來講,我打 lidemy 這六個字,每個字中間的間隔不會超過 250ms,所以 api 會在我打完 lidemy 的 y 以後過 250ms 才觸發,這樣就只會觸發一次而已。

因此你的任務呢,就是實作出一個 debounce 的函式,用起來會像這樣:

// 每當 input 的內容有變動,就呼叫 handleChange
$('input').change(handleChange)

// 讓原本發後端 api 的函式 debounce
const debouncedFn = debounce(getAutoSuggestions, 250)

function handleChange(e) {
  // 拿到 input 的值
  const value = e.target.value

  // 發 api 去後端拿搜尋建議,然後 render 出來
  // 細節我就不寫了
  // 在 250ms 內重複呼叫的話不會有反應
  debouncedFn(value)
}

memoize

假設我們現在有一個很複雜的函式叫做 complex,它的 input 會是一個數字 n 它是一個 pure function,所以傳進去的 input 如果一樣,output 也會一樣 因為它很複雜,所以每一次計算都至少要 2 秒才會回傳結果

console.log(complex(10)) // 等 2 秒才輸出結果

但因為它是 pure function,所以我們想在呼叫他的時候,順便把他的結果記下來 如果以後用同樣的參數去呼叫這個函式的話,就把之前記下來的結果回傳就好,因為一定是正確的 可是我們並不想直接去更動 complex 這個函式內部的東西 因此,我們需要一個叫做 memoize 的 function,它可以接收一個函式作為參數,並且幫這個函式加上「記憶」的功能,範例如下:

console.log(complex(10)) // 等 2 秒才輸出結果
console.log(complex(10)) // 等 2 秒才輸出結果

const memoizeFn = memoize(complex)
console.log(memoizeFn(10)) // 等 2 秒才輸出結果
console.log(memoizeFn(10)) // 立刻輸出結果,因為我們把之前 complex(10) 回傳過的值記起來了

而你的任務就是實作出這個 memoize 的函式

參考解答 [https://codesandbox.io/s/closure-homeworks-ans-i5mrd?file=/index.js](https://codesandbox.io/s/closure-homeworks-ans-i5mrd?file=/index.js) 比喻來源:[Debounce & Throttle — 那些前端開發應該要知道的小事(一)](https://medium.com/@alexian853/debounce-throttle-%E9%82%A3%E4%BA%9B%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC%E6%87%89%E8%A9%B2%E8%A6%81%E7%9F%A5%E9%81%93%E7%9A%84%E5%B0%8F%E4%BA%8B-%E4%B8%80-76a73a8cbc39)