逛知乎的时候发现 @DDDD 转了一张图, 这张图对 js 魔法的吐槽可谓非常到位. 下面, 我们就从这张图出发来详细讲讲 js.
数字类型与精度问题
虽然 js 是弱类型语言, 声明变量时也不需要显式指定类型. 但是, 数据本身依旧还是有类型的, 比如数字和字符串就是以不同形式存在的数据. 在 js 中, 所有数字的类型都为 number. 其中, 一个特殊的数字就是 NaN(Not a number), 虽然名字叫 "不是数", 但为了计算的一致性 (IEEE745 亦规定),NaN 依旧是数字类型的. 任何 NaN 参与的数字计算的结果都还是 NaN.(NaN-NaN!=0) 还需要注意的是, js 中被 0 除非但不会报错, 而且结果也不是 NaN(只有 0/0 是 NaN), 而是 Infinity(被除数为正)或 - Infinity(被除数为负).
由于将整数和浮点数统一处理, 所以 js 并不存在整数和浮点数的区别 -- 所有数字都以 64 位有符号浮点数 (IEEE745 格式) 的形式存储. 因此, 舍入误差是 js 数字类型的一个大坑. 最经典的当然就是 0.1+0.2!=0.3 了. 其实这不是 js 的锅, 大部分编程语言都有这个问题. 简单的解释可以查看 http://0.30000000000000004.com/ . 很多人对浮点数都有误解, 认为 0.1 是 , 所以可以正确的表示 0.1, 然而浮点数并不是这样. 在 2 为基数的情况下, 0.1=1/10 是一个无限循环小数, 所以在运算时会产生舍入误差. 想要进一步了解可以查阅有关数值计算的材料.
Max,Min 与函数参数
在 js 中, 函数参数也是一种魔法. 一般编程语言中, 形参具有类似 "约束" 的作用, 即实参的数量要与形参相符 (默认值除外). 但是 js 魔法并不需要形参和实参相匹配, 多的实参忽略, 少的就是 undefined. 事实上, js 还提供了一种访问参数的方法. 在函数体上下文中, js 提供了 arguments(类似 Python 的 * args) 以便参数的访问. 考虑到没有卵用的形参, js 函数的形参更 像 是一个别名.
在 Python 中, 函数重载可以通过默认值实现, 而在 js 中, 你可以随心所欲的解析 arguments, 可以说是很硬核了.
回到我们的 max 和 min.EMCAScript v3 之后, max 和 min 就支持任意数量参数的调用了. 从逻辑上考虑, 既然没有传入任何数, 那取最大的函数就不能返回一个能大于任何数的数, 所以返回 - Infinity 不无道理. min 亦然.
魔法操作符 +,-
为了更好的理解接下来的坑, 我们有必要先看看 +,- 两个操作符. 这俩操作符神奇就神奇在, 他们不仅仅是双目运算符, 也同时是单目运算符! 对于 +, 双目运算时其意义是数字加或字符串拼接. 这里有个很坑的地方, 就是只要参与运算的值不全是数字, 那么 + 就会被视为字符串拼接(String.concat), 从而把所有参数转换为字符串并进行拼接. 单目运算时,+ 被视为取正, 所有传入的参数都会被转换为数字并取正.(然而取正并没有任何卵用, 所以其实就是转为数字)
相比之下 - 就和蔼了许多, 双目是数值减, 单目这是取反.
无奈的解释器
下面这个魔法涉及到的东西很多, 不过我还是打算把它归类到解释器的范畴, 而且这个锅要丢给 REPL.{}+[]具歧义, 可以被理解为:
(作为值理解)对象{}+ 数组,+ 为双目
(作为代码块理解){}为代码块,+ 是单目
而这两种理解下的结果也是不同的. 而在 REPL 的上下文下, js 解释器采用了第二种理解. 而一旦上下文变化, 提示解释器这是表达式时, 解释器就会采用第一种理解. 比如 console.log({}+[])的结果就不再是 0 了.
隐式类型转换
之前在说 +,- 的时候, 我提到了一些隐式的转换, 其实这样的转换远不止出现在 +,-. 比如取反!, 就会转成布尔型再取反. 所以!+[]就是 true,![]是 false. 考虑到运算优先级, 最后得到 "truefalse".
作为一个弱类型语言, js 的逻辑是, 如果类型不对, 那就转过去.
== 与 ===
这是个老生常谈的东西了. 这里不多讲, 只要知道 == 可能会隐式转换类型就行.
我写这篇文章的目的并不是解释这张图, 因为这张图里的很多知识并不是实际编程所需要的(就像 i+++++i, 这些是我认为不重要的知识). 我是希望借这张图聊到一些 js 的语法特性, 以加深对 js 的理解. 这两点在我看来有本质的区别.
来源: http://www.tuicool.com/articles/nURFfuu