從js數字精度說到單元測試搭建再到尾遞歸

背景:

由於 js 浮點數存儲方式的問題,導致 js 數字進行運算時會有不準確的情況,最基礎的例子就是 0.1 + 0.2 !== 0.3,因此在進行價格單位轉換時,可能會遇到奇怪的問題。例如 19.9 * 100 !== 1990 再如 39.3 * 100 !== 3930。因此,在前端轉換價格單位時,因爲與服務端約定的是以分爲單位的整數,因此不論是前端還是服務端,肯定至少有一方會做取整的強制轉換,然後預期價格和實際價格就不同了。

之前雖然一直知道 0.1 + 0.2 !== 0.3 的例子,但從來沒在實際寫代碼時考慮過這種丟失的問題,只有出現錯誤了,纔開始意識到: 相信 js 的數字處理能力簡直是件危險的事。

解決辦法:

最直接的辦法自然是對價格的數字在某精度下進行四捨五入,例如我們需要到“分”,那麼在以元作爲單位的情況下,先乘 100,再進行四捨五入取整。

引出的其他問題:

如何驗證我們的方法是真的有效的呢?雖然從邏輯上進行分析“似乎”沒有問題,但由於涉及到比較重要的環節(錢),因此我們應當非常謹慎地對待這件事。解決方法便是 —— 上單元測試,增大可以試驗的數據量,從而保證方法的正確性。

 

實踐:

在確定了上述方向後,在項目中加入單元測試框架,用的mocha,至少目前來說,公共方法層面是“可以”甚至是“應當”編寫單元測試,保證方法的穩定性。也爲後續進行UI測試甚至接口測試打下基礎。

 

關於尾調用問題:

我們在方法中調用方法,就會產生一層調用棧,棧中存儲着調用層方法中的變量,隨着多一層調用,調用棧就增加一層,如果超過一個比較大的數,就會出現“棧溢出”的情況(stack overflow),爲了避免這種情況,es6 標準中提出了各平臺實現應當優化“尾調用”。之前寫過一些尾調用的方法進行循環深度遍歷,也一直以爲這種做法是有效的,直到今天寫價格的單元測試時,爲了提高覆蓋率,用尾遞歸的方式進行了超大量的逐個嘗試,結果就“爆棧”了,一直不得其解,二分嘗試後,發現chrome目前的調用棧層數大約在70000個,也就是函數的嵌套層數應該會被限制在70000層。後來查了一些資料,發現雖然 es6 就提出了優化尾調用問題,但各平臺並沒有實際放出尾調用的代碼結構方式,原因是擔心開發者寫了死循環卻不自知……。

 

尾遞歸的解法:

xxx說過,一切可以用尾遞歸的方式實現的方法,都可以使用 循環 實現。因此,就用循環實現吧。用循環會出現另外一個問題:我們常用的循環,都是同步結構,也就意味着是“阻塞”的,尾遞歸卻很容易實現異步遞歸。解法有兩個,其一,使用generator;其二,使用for await of的語句結構。當然也可以有第三種,使用 Symbol.Interator 自行實現迭代器,然後異步調用 next。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章