wtfjs:JavaScript玄学百科全书,不是段子,是ECMA规范照妖镜
37.7k星的wtfjs不是搞笑合集,而是一份披着荒诞外衣的ECMAScript规范注解。本文深度解析[] == ![]、'fail'字符串生成术、parseInt('Infinity', 19)等硬核案例,还原JS类型转换、ToPrimitive、抽象相等算法的真实执行路径,并揭示其作为「JS解剖室」的教学不可替代性。

嘿,各位前端战友、后端摸鱼人、以及被null == undefined绕晕的Java老哥——我是周小码,一个被Spring AOP织入人生、被Hibernate二级缓存缓存了青春的8年Java老兵。今天不聊JVM GC调优,也不讲MyBatis动态SQL怎么写得比SQL还像XML,咱来点清奇的:打开GitHub Trending榜首——wtfjs,一本用JavaScript写的《人类迷惑行为大赏》技术手册。
别误会,这真不是段子合集。它是一份披着搞笑外衣、藏着ECMAScript规范内核的硬核教材。就像你家楼下煎饼摊老板,表面是“加蛋加肠”,实则精通热力学传导(铁板温度)、流体力学(酱料滴落轨迹)和材料科学(葱花脆度临界点)。wtfjs也一样——每个[] == ![]背后,都站着第7.2.15节《抽象相等比较算法》;每行NaN === NaN // -> false,都在复刻IEEE 754浮点标准的幽灵。
安装?不,是「订阅」
wtfjs压根没打算让你把它当依赖引入。它走的是极客出版路线——npm只是它的发行渠道:
bash
$ npm install -g wtfjs
装完直接终端敲 wtfjs,它就用你的 $PAGER(比如less)给你弹出整本手册——这设计太懂程序员了:不是文档网站,是命令行里的《JavaScript玄学百科全书》。没有package.json里一堆devDependencies,没有node_modules里藏了23个版本的lodash,干净得像刚从TypeScript编译器里逃出来的ES5代码。
Hello WTF?——从最荒诞处开始
它不给你console.log('Hello World'),它给你这个:
js
[] == ![]; // -> true
初看血压飙升,细读毛骨悚然。为什么空数组等于“非空数组”?因为JS在==时会偷偷把两边都转成数字:[]变成0(数组的ToNumber规则),![]先转布尔false再转数字0,于是0 == 0成立。这根本不是bug,是ECMA-262第7.2.15节亲手盖章的“合法胡闹”。
再来看更绝的:
js
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];
// -> 'fail'
这哪是代码?这是用JS写的摩斯电码!它靠![] + []生成字符串'false',再用+[](转数字0)和+!+[](!+[]是false,再+转为0,+!+[]就是+0→0?不对,+!+[]其实是+(!![])→+true→1!),精准索引字符拼出'fail'。这已经不是编程,是JS字节码级的行为艺术。
那些你以为懂、其实被坑惨的「常识」
-
null是对象?typeof null === 'object'—— 这是V8引擎祖传bug,1995年Brendan Eich写第一版JS时,把null的底层表示设为全0,而当时对象指针也是全0,于是typeof就认错了。规范至今没修,因为修了会崩掉百万行线上代码。Java里null instanceof Object直接报错,JS却让它活成了哲学命题。 -
0.1 + 0.2 !== 0.3? 不是JS的锅,是IEEE 754二进制浮点数在用尽全力模拟十进制。就像你让米其林厨师用面粉做寿司——不是手艺不行,是原料根本不匹配。wtfjs告诉你:这不是缺陷,是物理定律在代码里的投影。 -
parseInt("Infinity", 19)居然等于18? 因为I是19进制下的第18位(0~9, A~I)。parseInt根本不管你在解析什么,它只忠实地按进制表翻译字符。这哪是函数?这是个没有感情的进制翻译机。
架构?不存在的——它就是一份活的规范注解
wtfjs没有架构图,没有模块划分,没有CI/CD流水线。它的「架构」就是GitHub仓库结构:一个README.md,几十个带锚点的标题,每个标题下是代码块+解释+规范链接。它用最原始的方式践行了Unix哲学:“一个程序只做好一件事”。它不做运行时检测,不提供API,不封装工具类——它只负责展示,把规范里冷冰冰的算法,变成你终端里会动的示例。
这种设计对教学价值拉满:你看Array.prototype.sort()默认按字符串排序导致[10, 1, 3] → [1, 10, 3],立刻明白为啥生产环境必须传compareFn;看到{a: 1}{b: 2}在控制台报错,马上理解{}在不同上下文里是对象字面量还是代码块——这种认知冲击,比读十页MDN文档都管用。
Java老兵的真心话:这玩意儿该不该学?
坦白说,如果你的目标是“快速上线一个React管理后台”,wtfjs的优先级应该排在“学会用Chrome DevTools打断点”之后。但如果你的目标是“三年内成为能给V8提PR的前端架构师”,那它就是你的《九阴真经》下卷。
我在Java里天天写Objects.equals(a, b),就是因为怕==在包装类型上翻车;而JS开发者却要主动拥抱==的隐式转换,再用Object.is()去修补裂缝。这种思维差异,本质是语言哲学的不同:Java追求确定性(final、private、类型擦除前的泛型),JS拥抱动态性(鸭子类型、原型链、运行时修改Function.prototype)。
所以我的建议是:别把它当教程,当成「JS解剖室」。每周挑3个例子,关掉浏览器,纯手敲,然后查ECMA-262原文。当你某天发现[].toString() === ''和{}.toString() === '[object Object]'背后是[[ToString]]内部方法的差异化实现时——恭喜,你已经不是在写JS,而是在和JS引擎对话了。
最后送一句wtfjs精神内核:Don’t panic. Just read the spec.(别慌,去读规范)——毕竟,所有WTF的终点,都是Aha!。