駁《我不是很懂 Node.js 社區的 DRY 文化》

今天在羣裏有人討論方老師的文章《我不是很懂 Node.js 社區的 DRY 文化》,我也看了一遍,槽點太多,不知道如何下筆。html

方老師分析了幾個依賴最多的 npm 包,每一個都只有不到百行代碼。git

好比 is-odd,每週下載 300 萬次,可是隻有核心 5 行代碼。並且依賴了每週下載 1000 萬次的 is-number 庫。程序員

得出了一個結論:github

  1. 原來有這麼多 JS 程序員不會判斷奇數
  2. 只要 markdown 寫得漂亮,就能迷倒 JS 程序員
  3. 1 + '1' 的問題一直在困擾 JS 程序員,我要不要寫一個 add() 庫解決這個問題呢

首先第一條:算法

原來有這麼多 JS 程序員不會判斷奇數。

其實不只僅是 JS 程序員,大部分程序員都不會準確的判斷奇數。npm

你寫安全

const isOdd = x => x % 2 === 1;

這是小學的知識,除以 2,若是除不盡(有餘數)那麼就是奇數。正由於知識點很簡單,因此給人一種隨便一個程序員都會判斷的錯覺。微信

如今咱們假設用戶傳入的參數必定是數字。markdown

即使如此,這個函數依然不能正確判斷奇數。由於 -3 % 2 的結果是 -1函數

有人說那就這麼寫:

const isOdd = x => x % 2 !== 0;

隨便一個小數就被判斷爲奇數了。更不用說浮點數中的妖怪 NaNInfinity 了。

那麼是否是對 NaNInfinity 直接返回 falst,而後把 -1 的判斷也加上去就好了:

const isOdd = x => x % 2 === 1 || x % 2 === -1;

也是圖樣

9007199254740991 % 2 === 1
9007199254740992 % 2 === 0
9007199254740993 % 2 === 0
9007199254740994 % 2 === 0
9007199254740995 % 2 === 0
// 後面的都是 0

爲何從 9007199254740991 開始呢?由於這個值是 Number.MAX_SAFE_INTEGER,是 2 ** 53 - 1

那回過頭來看看 is-odd 庫是怎麼實現的呢?

!!(~~i & 1)

~~i 用於把字符串轉換爲整數,和 1 進行按位與運算判斷最後一位是 1 仍是 0

很遺憾,也有問題。😔 由於在字符串轉整數的時候精度就丟失了。

若是有誰想造輪子,能夠寫一個 better-is-odd,能夠把字符串 '9007199254740995' 判斷爲奇數,可是對於數字 9007199254740995 也是無能爲力。等着 proposal-bigint 提案吧。

不只僅是判斷奇數,單純的判斷一個字符串是否是數字就能夠難倒一大片 JS 程序員(其它語言程序員也同樣)。

is-number 庫核心代碼不到 10 行。方老師只關注了庫的源代碼,可是咱們若是看一看他的 test case,就決定要使用這個庫了。

做者爲這 10 行代碼寫了 108 行的測試用例,來保證這個函數的功能是正確的。

我在以前的文章百行代碼,千行測試裏面曾寫過:

不要重複發明輪子。

不少大牛推薦咱們「造輪子」,可是造輪子的目的是爲了學習,而不是使用,尤爲不要用在生產環境。

造個輪子很簡單,可是你非要把本身的輪子安在汽車上,開上路,那確定是一個安全隱患。

有不少人會說,「既然本身能夠寫一個,爲何非要用別人的?」 還有人以爲,有些很是小的功能不須要使用別人的。

不少人還會藉此吐槽 leftpad 模塊,可是平心而論,你本身能徒手這一個沒有 bug 且高性能的 leftpad 函數嗎?

前幾天咱們項目組就遇到了一次,其實功能很簡單,一個頁面分享出去,並使用 url 攜帶參數。好比:

aaa.html?id=123456

看似很簡單的一個需求,可是真正本身寫一個卻不簡單。

  1. 查找「=」字符,而後截取後面的?
  2. split("="),而後去第二個
  3. ……

不到 10 行代碼就寫完了。

第一次分享到微信是正常,把分享出去的頁面再次轉發分享,頁面錯誤。

由於微信會在 URL 後面添加一些額外的參數,一樣,不一樣的平臺都會有不一樣形式的添加參數方式,有的加 &,有的加 #,不論加什麼都會致使解析的失敗。

歸根結底是咱們寫的解析函數有 bug,咱們從新造了一個有 bug 的輪子。

解決方式就是:

npm i qs

麻雀雖小,五臟俱全。看看 github 源碼,「百行代碼,千行測試」。絕對比本身寫的代碼靠譜。

我寫這篇文章不是爲了推薦這個 qs 庫,而是告訴你們不要重複造輪子用在生產環境,平時你們多造輪子用來學習。

在回過頭來看看 is-number 庫,不只僅有 100 多行的 test case,還有一個目錄 benchmark。這裏面的代碼我沒有數,可是光看文件數量就有 10 個以上。也就是說做者不只僅保證了這個函數的運行結果沒有問題,更保證了這個函數的性能。

咱們爲何要使用這個庫,由於做者爲了他的 10 行代碼,寫了幾百行的其它代碼來保證質量。

做者 9 天前還發布了新版,20 天前還優化了字符串轉數字的性能。

再看看方老師說的第二條:

只要 markdown 寫得漂亮,就能迷倒 JS 程序員。
這些包的 markdown 代碼遠遠多於 JS 代碼,可能它們的 markdown 更值得咱們學習

Redux 號稱百行代碼,千行文檔,一共就導出了 5 個函數。

並且 markdown 寫的漂亮也是頗有必要的,不然你不知道下面的代碼到底輸出什麼

isOdd(' 12')
isOdd('一')
isOdd('①')
isOdd('Odd')

第三條:

1 + '1' 的問題一直在困擾 JS 程序員,我要不要寫一個 add() 庫解決這個問題呢

不能。

我是認真的!由於 npm 已經有一個 add 庫了,名字被別人佔用了,因此你只能叫別的名字了。

雖然是一個小衆的庫,可是每週也有近一萬的下載量。這個庫實現了 JavaScript 中的浮點數加法的 Rump-Ogita-Oishi 算法。

好比有以下浮點數:

const nums = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]

把這些數累加

nums.reduce((a,b) => a+b);

結果是:

15.299999999999999

而使用 Rump-Ogita-Oishi 算法:

add(nums) === 15.3

再看看 benchmark (OS X 10.9.4, 2 GHz Core i7, 8GB DDR3 1600Mhz RAM):

add-precise x 1,400,712 ops/sec ±3.31% (89 runs sampled)
add-dumb x 24,268,034 ops/sec ±3.96% (80 runs sampled)
native x 94,957,251 ops/sec ±2.94% (85 runs sampled)
native is ~67.8 times faster than add-precise

最後再重申通常:Don't Repeat Yourself

相關文章
相關標籤/搜索