驳《我不是很懂 Node.js 社区的 DRY 文化》

101

今天在群里有人讨论方老师的文章《我不是很懂 Node.js 社区的 DRY 文化》,我也看了一遍,槽点太多,不知道如何下笔。

方老师分析了几个依赖最多的 npm 包,每个都只有不到百行代码。

比如 is-odd,每周下载 300 万次,但是只有核心 5 行代码。而且依赖了每周下载 1000 万次的 is-number 库。

得出了一个结论:

  1. 原来有这么多 JS 程序员不会判断奇数
  2. 只要 markdown 写得漂亮,就能迷倒 JS 程序员
  3. 1 + '1' 的问题一直在困扰 JS 程序员,我要不要写一个 add() 库解决这个问题呢

首先第一条:

原来有这么多 JS 程序员不会判断奇数。

其实不仅仅是 JS 程序员,大部分程序员都不会准确的判断奇数。

你写

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

这是小学的知识,除以 2,如果除不尽(有余数)那么就是奇数。正因为知识点很简单,所以给人一种随便一个程序员都会判断的错觉。

现在我们假设用户传入的参数一定是数字。

即便如此,这个函数依然不能正确判断奇数。因为 -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


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

33 条评论
Myou_Aki · 2018年04月19日

“很多大牛推荐我们“造轮子”,但是造轮子的目的是为了学习,而不是使用,尤其不要用在生产环境”,赞同!

+8 回复

0

用在生产环境也不是不可吧,需要对自己的轮子有信心

moyuyc · 2018年04月24日
范捷琦Jackie · 2018年04月19日

说句实话,看到那个is-odd,真心觉得自己不会判断奇数。。。

+3 回复

奇点 · 2018年04月19日

怕就怕万事去找库,这和万事去百度,差别不太大

+3 回复

0

省下时间去做更有意义的事情不好吗哈哈哈哈

sirormy · 2018年04月20日
0

@sirormy 道理是这样没错,但是容易出现一个极端情况是,脱离了那些库就干不了事了

奇点 · 2018年04月21日
leekafai · 2018年04月19日

自我口嗨一下,请大家去这位“方老师”的知乎里面看他的动态,里面水分有多少各自都能分辨。
我在前端方面的造诣并没有这位“方老师”那么高深,但是有许多比这位“方老师”要高得多的人,在社区中的表现要好很多。
我对这位“方老师”粉转黑,也是因此而已:水多,口嗨。

+2 回复

0

方应杭?

damon陈先生 · 2018年04月19日
0
leekafai · 2018年04月19日
1

@damon陈先生
方应坑

justjavac 作者 · 2018年04月19日
_Nathan · 2018年04月20日

总觉得在论坛上讨论这类语言特性的问题没有任何意义。应该去官方的项目上去提issue。在这里讨论官方又看不到

+1 回复

不死小强 · 2018年04月19日

看了那个作者的文章,感觉他没考虑太多,先喷再说~

回复

鱼肚 · 2018年04月19日

这个“方老师”也是典型的自以为聪明

回复

默凡 · 2018年04月19日

确实,自己造的轮子用生产环境太容易出问题了。。。而且效率还不高,但也不能形成“轮子依赖症”,因为有的轮子不一定适合你

回复

不辣的老姜 · 2018年04月19日

看完之后觉得javascript真是个糟糕透了语言。前端是没办法,后端千万别用。

回复

悠然见南山 · 2018年04月19日

个人觉得方应杭说的并不是没有道理,npm生态圈确实有点糟糕...

回复

chen_aaron · 2018年04月19日

我觉得这恰恰证明了node社区的繁荣。

回复

0

繁榮的語言和社區多了去了, 也沒見給is_odd寫庫, 這個理由不好.

陈力 · 2018年04月19日
杰枫Jeff · 2018年04月20日

我自己就喜欢造轮子,但是让别人用的时候一定会测试测试再测试,否则翻车的话场面就不好看了。

回复

waynebruce · 2018年04月20日

这难道 不是弱类型语言 隐式转换的锅吗? 前端TS,后端就 ts-node 不就行了吗

回复

laoLiueizo · 2018年04月20日

这人出了名的跳,自以为自己是出色的段子手,我反正觉得很恶心

回复

划水咸鱼 · 2018年04月21日

现在回头看看自己公司项目里跑着的自己造的轮子.有点紧张啊.....

回复

陈天赐 · 2018年04月22日

在下平生最怕的就是写教程的(虽然自己也写,打脸ing)。。。
感觉就是在反复强调别人讲过的段子。。
自己分析理解的心得并不适用于所有人。。。。
一向信奉 苦尽甘来 。
最怕的就是 零基础教程、速成大法。。。
OMG ?

回复

Jemair · 2018年04月23日

社区还是需要有方方这种人存在的。这样才能激励你们这帮平时不爱讲话的死宅多写优质文章啊~

回复

0

哈哈哈,思路奇特啊

justjavac 作者 · 2018年05月04日
lovedebug · 2018年04月25日

正解,给ls打气

回复

webxing · 2018年04月27日

嗯 我们老大也是说尽量用别人造好的成熟的轮子, 自己手动去造的总归考虑的没那么周全

回复

代码宇宙 · 2018年04月28日

请问,什么是Rump-Ogita-Oishi算法,没有查到相关资料。

回复

0
justjavac 作者 · 2018年05月04日
载入中...