正式说明
以Facebook为例,Facebook有一种数据类型叫做FBID,是一个64位的整数,用作任何对象的唯一ID。
在Facebook把无类型的PHP转化为有类型的Hack之前,很多人不知道FBID是整数还是字符串,于是有些函数用整数接收FBID,另外一些函数用字符串。
到最后,函数间传递的FBID到底是什么类型已经说不清楚。
导致的结果就是:你写一个函数,中间接收了一个FBID的数组,你永远不可能知道FBID属于哪种类型,甚至可能是混合类型的。
在语义上这绝对是有问题的,但神奇的PHP在做整数和字符串“==”比较时会自动把字符串隐式转换为整数,所以原本不应该相等的两种类型FBID可能会被判定为相等。不过如果用===的话,不同类型自然不相等。因为很多人不注意细节,使用==而非===,所以如果假设你接收了一个FBID的数组,然后调用别人的函数来去重,你猜“123”和123会不会被当作重复而去掉?你不可能猜到,因为你不知道别人用什么做对比。
这使得当时Facebook的PHP代码存在大量难以发现的bug,而且没人敢去修。你说你这里FBID一定是整数,然后收到字符串就抛异常?你猜你这一改Facebook哪些功能会挂掉?你不可能知道……所以你不敢改。
最终Facebook把PHP逐步转化为Hack,把FBID存储为整数,大家就可以放心地把FBID传来传去不用担心出问题了。如果你尝试拿FBID跟另外一个字符串或整数比较,你的代码在静态分析阶段就会报错。你一定要比较,就必须进行显式类型转换后再做比较。
同理,Facebook把JavaScript改造为Flow,但FBID在JavaScript里面必须存储为字符串,因为JavaScript存储不了64位整数,可能会转换为浮点数存储,然后丢失精度。在此之前,有人不知道FBID在JavaScript不能存为整数,然后引发各种难以复现的bug。
如果你不懂Hack或Flow,可以用现在主流的TypeScript做参考。偶认为这是现在最好的折衷方案。语言本身是支持动态类型的,但通过类型注释来让类型稳定的变量和参数变为有类型,你不加类型注释的时候还能灵活使用动态类型。
为什么要保留动态类型?
因为你在开发新功能时,可能你只是在探索,具体数据结构还没有定下来,不需要声明完整的数据结构类型信息能为你节省不少改来改去的时间。与之对比的是那些C++和Java操作JSON的代码,必须处处声明现在预期读取出来的变量是什么类型,你稍微改一下JSON的结构就要改对应的类型声明,这真的很麻烦。
以上,希望对你有用!