JavaScript 变量类型考——论代码世界中的浮世绘
一、楔子:这无物的孱头
大抵世间万物皆有其名,人唤作张三李四,物称作桌椅板凳。偏生这 JavaScript 的变量,戴着张”弱类型”的假面,在代码的胡同里游荡。初时以为它温顺如猫,待得深究,方知是只未拴绳的疯犬,咬人时连声招呼也不打。
我常想,若许先生当年设计这门语言时,多抽两口鸦片清醒清醒,也不至教后人在这类型泥潭里打滚。你看那 Java 端着架子讲阶级,C++ 摆弄指针论血统,偏这 JavaScript 像个市井泼皮,逮着什么便往变量里塞——塞得进是造化,塞不进便是一地狼藉。
二、原始类型七宗罪
1. Number:数字的荒诞剧
0.1 + 0.2 === 0.3 // 竟得个 false
浮点数的把戏,原是计算机的痼疾。但 JavaScript 偏要学那孔乙己,将 IEEE 754 的”茴”字写法当作宝贝供着。开发者若问精度,便涨红了脸,额上的青筋条条绽出,争辩道:“浮点误差不能算错…科学计算的事,能叫错么?“
2. String:字符的困局
'你'.length // 显出 1 来'𠮷'.length // 却变成 2
Unicode 本是开天辟地的功德,偏生让 JS 的 UCS-2 实现搅成了浆糊。那些个码位、码点的学问,倒像是未庄人谈论革命——听着热闹,实则一知半解。Emoji 与生僻字在字符串里打架,正则表达式在旁冷笑,这场景比阿Q画押还要滑稽三分。
3. Boolean:是非的黑白道
!![] // true!!0 // false
真与假的门槛,在这里低得可笑。空数组是真,零是假,空对象也是真。这等判断标准,倒像是赵太爷家的规矩——他说你配姓赵,你便姓得;他说不配,你便不配。类型转换的潜规则,比闰土脖子上的银项圈还要沉重。
4. Null 与 Undefined:未庄的两条影子
typeof undefined // 'undefined'typeof null // 'object'
分明是双生子,偏要扮作陌路人。一个自称”未定义”的清高,一个混迹对象堆的龌龊。这二者在代码里游荡,比祥林嫂的絮叨还要惹人厌:
let empty;empty === undefined // trueempty === null // false
开发者若问区别,便如问未庄的赵太爷与钱太爷谁更高贵——横竖都是吃人的主。偏生有那等”最佳实践”,教人用三目运算符作判官:
value == null // 竟能同时捉住两个幽灵
这等暧昧的勾当,倒像是阿Q摸小尼姑的头皮——浑水摸鱼罢了。
5. Symbol:新青年的长衫
const key = Symbol('密钥');const obj = { [key]: '秘藏' };Object.keys(obj) // 空空如也
说是独一无二的身份,却活成了代码世界的闰土。那些个描述符,像极了孔乙己写在粉板上的”茴”字,除了自我安慰别无他用。浏览器开发者工具里闪烁的Symbol身影,倒像是未庄茶馆的茶渍——看得见,摸不着。
更可笑的是全局注册表的把戏:
Symbol.for('命脉') === Symbol.for('命脉') // true
这所谓的”全局唯一”,恰似赵白眼在城门口挂的招牌——明写着”新党”,暗地里仍是旧把式。
6. BigInt:钱庄新规
9007199254740991 + 1 // 90071992547409929007199254740991n + 1n // 9007199254740992n9007199254740992 + 1 // 9007199254740992(精度丢失)
为解决Number的痼疾,又造出个新怪物。后缀n的写法,活脱脱是假洋鬼子的”NO”字辫子。与普通数字泾渭分明:
1n + 1 // 报错!比未庄的族规还要森严
这等级制度,倒应了九斤老太的牢骚:“现在的长毛,连计算都要分个贵贱。“
三、对象类型浮世绘
1. Object:众生相
const obj = { key: 'value' };obj.undefinedProperty = undefined;if (obj.undefinedProperty) { // 不会执行 console.log('obj.undefinedProperty');}if ('undefinedProperty' in obj) { // 执行 console.log('undefinedProperty in obj');}const son = Object.create(obj);if (son.key) { // 执行 console.log('son.key');}if ('undefinedProperty' in son) { // 执行 console.log('undefinedProperty in son');}if (son.hasOwnProperty('key')) { // 不会执行 console.log('key hasOwnProperty son');}
这对象活脱脱是未庄的茶馆,三教九流皆可入座。明明存着 undefined 的座位,偏要装出”无”的姿态。hasOwnProperty 像极了掌柜的势利眼,in 操作符则是跑堂的谄笑,原型链更似乡绅的族谱——翻开来尽是些发霉的旧事。
2. Array:秩序的假面
const arr = [1, 2, 3];arr[10] = 11;arr.map(v => 'WTF'); // ['WTF', 'WTF', 'WTF', empty × 7, 'WTF']
说是数组,倒像破庙里的和尚——有名无实。稀疏数组的窟窿比华老栓茶馆的板凳还多,forEach 跳过空位如同阿Q避讳”癞”字。那些个 map、filter 的方法,不过是给破袈裟打补丁,终究遮不住 length 属性的荒唐。
3. Function:变形的戏法
function fn() {}fn.arbitraryProp = 'value';
函数在这里成了千面人,今日扮作可执行代码,明日又充任对象容器。arguments 像闰土手里的钢叉,既陈旧又危险。闭包更似土谷祠的蜘蛛网,捕得住变量,却困住了内存。
四、类型判断的罗生门
1. typeof 的糊涂账
typeof null // 'object'typeof [] // 'object'
这操作符活脱脱是个糊涂差役,将 null 错认作 object,把数组也归入同类。倒像是衙门里的冤案,错判了还要嘴硬,说是”历史遗留问题”。
2. instanceof 的世袭制
[] instanceof Object // true
原型链的世袭,比未庄的宗法还要森严。数组本是平头百姓,偏要认 Object 作祖宗。这般血统论,倒应了九斤老太的话:“一代不如一代”。
3. 全等(===)的虚伪
NaN === NaN // false+0 === -0 // true
说是严格比较,却对 NaN 摆起架子,对正负零又和起稀泥。这规矩立得,比假洋鬼子的”洪哥约我”还要荒唐。
五、特殊值现形记
1. NaN:荒诞派的杰作
Math.sqrt(-1) // NaNtypeof NaN // 'number'
分明不是数(Not a Number),偏要归入数字门楣。这等指鹿为马的本事,比赵贵翁还要高明三分。更可笑其自视清高:
NaN === NaN // false
两个非数相遇,竟如未庄的闲汉互啐口水——谁也不认谁。检测之法更是荒诞:
isNaN("字符串") // trueNumber.isNaN("字符串") // false
新旧两种检测,倒像是七斤与赵七爷的辫子——剪与不剪都是错。
2. Infinity:僭越者的狂欢
1 / 0 // Infinity-1 / 0 // -Infinity
数学的禁忌,在这里成了狂欢的借口。Number.MAX_VALUE 像个纸糊的牢笼:
Number.MAX_VALUE * 2 // Infinity
这无限膨胀的野心,倒像是未庄的田亩——说是皇上的疆土,实则任人践踏。
六、结语:铁屋中的呐喊
呜呼!类型系统的乱象,恰似未庄的祠堂——旧神未去,新鬼已来。我等开发者,不过是在代码的月光下,书写着新的《药》。
在这类型混乱的围城里,TypeScript 像是一扇透光的窗。但终究是治标不治本的膏药,贴不住 JavaScript 的顽疾。我们这些码农,正如狂人般在类型系统里挣扎,写下的注释倒比代码还长。
然则出路何在?或曰:世上本没有类型,代码写多了,便有了类型。这道理,怕是连孔乙己听了,也要摇头叹一句:“多乎哉?不多也。”