Skip to content

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 // true
empty === 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 // 9007199254740992
9007199254740991n + 1n // 9007199254740992n
9007199254740992 + 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) // NaN
typeof NaN // 'number'

分明不是数(Not a Number),偏要归入数字门楣。这等指鹿为马的本事,比赵贵翁还要高明三分。更可笑其自视清高:

NaN === NaN // false

两个非数相遇,竟如未庄的闲汉互啐口水——谁也不认谁。检测之法更是荒诞:

isNaN("字符串") // true
Number.isNaN("字符串") // false

新旧两种检测,倒像是七斤与赵七爷的辫子——剪与不剪都是错。

2. Infinity:僭越者的狂欢

1 / 0 // Infinity
-1 / 0 // -Infinity

数学的禁忌,在这里成了狂欢的借口。Number.MAX_VALUE 像个纸糊的牢笼:

Number.MAX_VALUE * 2 // Infinity

这无限膨胀的野心,倒像是未庄的田亩——说是皇上的疆土,实则任人践踏。

六、结语:铁屋中的呐喊

呜呼!类型系统的乱象,恰似未庄的祠堂——旧神未去,新鬼已来。我等开发者,不过是在代码的月光下,书写着新的《药》。

在这类型混乱的围城里,TypeScript 像是一扇透光的窗。但终究是治标不治本的膏药,贴不住 JavaScript 的顽疾。我们这些码农,正如狂人般在类型系统里挣扎,写下的注释倒比代码还长。

然则出路何在?或曰:世上本没有类型,代码写多了,便有了类型。这道理,怕是连孔乙己听了,也要摇头叹一句:“多乎哉?不多也。”