JavaScript中void 0和undefined有什么区别
在 JavaScript 中,void 0 与 undefined 的核心区别在于:undefined 是一个标识符(变量名),而 void 是一个一元运算符。void 0 通过执行求值并丢弃返回结果,恒定输出原始值 undefined;而标识符 undefined 在历史版本或局部作用域中存在被意外重写的风险。本文将深入拆解两者的本质差异、运行机制、代码体积考量及现代开发中的选择策略。
一、本质区别:标识符与一元运算符
很多开发者在阅读开源库源码(如早期的 Underscore、Lodash 或 Babel 编译产物)时,常会看到 void 0 代替直接写 undefined。理解这种写法的根本起点,是认清两者在语法层面的分类。
undefined是标识符:它不是保留字,而是一个全局属性名,默认指向原始值undefined。既然它是标识符,语法解析时就会将其当作普通变量对待。void是运算符:void <expression>是一元表达式,负责对其后的表达式进行求值,然后无条件返回原始值undefined。传入0只是因为它是最短且最轻量的求值表达式。
二、核心差异一:可修改性与作用域遮蔽
造成 void 0 被广泛使用的首要原因,就是 undefined 可能会被修改或遮蔽。
1. 全局作用域(ECMAScript 3 历史遗留)
在 ECMAScript 3(例如较旧的 IE8 浏览器环境)中,全局对象上的 window.undefined 是可读可写的。只要执行 undefined = true,后续所有直接判断 value === undefined 的逻辑都会崩塌。
虽然从 ECMAScript 5 开始,全局的 undefined 已经被配置为不可写属性(writable: false),直接修改 window.undefined = "test" 会在非严格模式下静默失效或在严格模式下抛出 TypeError,但局部作用域依然存在隐患。
2. 局部作用域的变量遮蔽(Shadowing)
即使在现代浏览器和 Node.js 环境中,undefined 也并非语言保留字。在任何函数或代码块内部,开发者依然可以合法地声明一个名为 undefined 的局部变量:
function checkStatus(status) {
// 在局部作用域中声明一个名为 undefined 的变量
var undefined = "error";
// 此处读取的是局部变量,而非原本的原始值
console.log(status === undefined);
}
checkStatus(undefined);
// 输出结果为 false,因为 status 是真正的 undefined,而局部 undefined 变成了字符串 "error"
如果使用 void 0,无论当前作用域中是否遮蔽了变量,表达式都会由引擎内部计算并返回真正的空值,保证逻辑判断的确定性。
三、核心差异二:打包产物与字符开销
在前端工程化中,构建工具(如 Terser、esbuild、UglifyJS、SWC)非常偏爱 void 0。这主要是出于字符长度与压缩率的权衡:
undefined占用 9 个字符。void 0只占用 6 个字符。
在没有 Gzip 之前,节省 3 个字符可以实打实减小代码体积。而在经过 Gzip 压缩后,虽然重复词汇的体积惩罚会显著降低,但通过编译器统一替换为 void 0 依然能建立更高的模式重复度,同时完全杜绝变量遮蔽对业务逻辑的破坏。
四、为什么是 void 0,而不是 void 1 或其他操作数?
void 运算符要求右侧必须跟一个表达式。在语法上,以下所有写法产生的结果完全等价:
void 0; // undefined
void 1; // undefined
void (true); // undefined
void "hello"; // undefined
void (() => {}); // undefined
选择 0 仅仅因为它具有最短的字符长度(1 位)与最低的求值开销。数字字面量 0 在语法解析时无需任何复杂运算即可快速返回,因此 void 0 成为行业约定的标准范式。
void 可以跟任何表达式,但必须注意运算符优先级与副作用。例如 void a = 1 会引发语法错误,正确写法应为 void (a = 1);此外,void doSomething() 会真实执行函数体,仅将其返回值替换为 undefined。
五、全维度对比:特性速查
为了更直观地区分两者,我们将语法特征、运行机制及工程行为汇总如下:
| 对比维度 | undefined |
void 0 |
|---|---|---|
| 语法分类 | 标识符(全局变量属性) | 一元表达式(操作符 + 字面量) |
| 是否保留字 | 否(可作为局部变量名声明) | 是(void 为语言内置关键字) |
| 返回值可靠性 | 现代全局只读,但局部可能被遮蔽 | 100% 可靠,恒定返回原生 undefined |
| 源码可读性 | 极佳,语义直观通用 | 一般,对初学者存在认知门槛 |
| 字符体积 | 9 字符 | 6 字符 |
| 主要出场场景 | 手写业务代码、参数默认值、类型注解 | Babel/SWC 编译产物、压缩工具、老旧脚本 |
六、现代前端开发的实践建议
理解了原理后,很多开发者会疑惑:我们在当前日常写代码时,还需要刻意写 void 0 吗?
答案是:普通业务开发中建议直接使用 undefined,无需手动书写 void 0。
-
现代作用域规避了误伤:在开启严格模式(
"use strict")或基于 ES Module 组织的项目中,声明var undefined = ...属于极其不规范的操作,ESLint 默认规则(如no-shadow-restricted-names)会直接报错拦截。 -
构建工具会自动处理:你在源码中保持
undefined的高可读性,构建阶段 Vite、Webpack 等打包器的混淆插件(Terser、esbuild)会自动将满足条件的代码降级优化为void 0。 -
TypeScript 的普及:在 TypeScript 中,
undefined作为类型声明是必不可少的;滥用void 0替代空值判断反而会导致代码风格割裂。
总结来说,undefined 赢在可读性与语言语义,而 void 0 胜在纯粹的防御性与不可变性。了解这个差异,既能帮助你在阅读底层库与编译代码时消除困惑,也能更清晰地把握 JavaScript 作用域与表达式求值的本质逻辑。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













