在早期 JavaScript 环境中,undefined 并非一个不可变的“原始值”。开发者可以在全局作用域中对其重新赋值,从而导致对“未定义”状态的不确定判断。
为了解决这一历史遗留问题,社区中出现了使用 void 运算符来 稳定 地获取原生的 undefined 值的做法,典型写法是 void 0。
历史背景与问题起因
1、undefined 不是关键字,也不是保留字
undefined 是 全局对象 window 的一个只读属性(历史问题)。
全局环境
局部环境
const m = () => {
var undefined = 10;
console.log(undefined); // 10
};
m();
因此当一个函数返回 undefined 时,会有隐患。
2、导致“防御式编程”负担
为了避免全局 undefined 被污染,开发者通常要先做:
var undef; // 本地变量 undef 默认为 undefined
if (foo === undef) { /* … */ }
使得代码冗长且不直观。
因此,一个可靠且不可被重写的方式来获取原生 undefined 值,变得尤为重要。
void 运算符简介
1、语法
void <expression>
对 <expression> 进行求值(可以产生副作用),但始终返回 原始的 undefined。
void 0 // 返回 undefined
void (1+2) // 执行 1+2,但丢弃结果,返回 undefined
2、优先级与使用注意
void 的优先级较低,通常需要使用小括号来明确:
void 2 === 2 // 相当于 (void 2) === 2,返回 false
void (2 === 2) // 返回 undefined
3、为什么是 void 0?void 1、void 3 可以吗?
- void 对操作数的值毫不关心:无论是 void 0、void 1、void (Math.random()),最终结果都相同:undefined。
- 之所以常用 0,主要因为:简洁:单字符文字减少键入量与视觉干扰。无副作用:0 为纯数值常量,无函数调用、属性读取等开销及异常风险。
因此,技术上以 void 1、void 3 或其它表达式替代 void 0 都合法,但并不常见。
void 0 vs undefined
项目 | undefined | void 0 |
---|---|---|
可写性 | 非严格模式下可被覆盖 | 永远不可覆盖 |
代码可读性 | 直观、语义明确 | 需理解 void 语义 |
字节数消耗 | 9 个字符 | 6 个字符(void 0) |
执行开销 | 与普通变量访问一致 | 多一个操作符,开销微乎其微 |
注意一点:
在 JavaScript 中,undefined 是一个原始值(primitive),表示一个变量已经声明但尚未赋值。
let a;
console.log(a); // undefined
除此之外,undefined 在 ECMAScript 规范中是:
- 一个全局变量(但不是保留字)
- 可以被重新赋值或重新声明(在某些情况下)
- 它的值是 undefined(本身就是字面量)
为什么输出不一样呢?
const undefined = 1;
console.log(undefined); // 1
let undefined = 1;
console.log(undefined); // 1
var undefined = 1;
console.log(undefined); // 1
undefined = 1;
console.log(undefined); // undefined
1、const、let
这里的 undefined 是自定义的一个块级常量变量。它遮蔽了全局的 undefined。所以 console.log(undefined) 打印的是声明的常量值 1。
2、var
用 var 声明的变量会被提升到函数作用域或全局作用域中。仍然会覆盖全局对象上的 undefined。所以它变成了赋值的 1。注意:这也意味着污染了全局变量。
3、undefined = 1
- 在 严格模式下,这段代码会报错,因为 undefined 是只读的。
- 在 非严格模式下(默认),可以写 undefined = 1,但实际上赋值不会生效!
(function () {
undefined = 1;
console.log(undefined); // undefined
})();
为什么呢?
- undefined 虽然不是保留字,但在某些环境中,JavaScript 不会真的改变它的值,或者试图改的时候根本没有效果。
- 从 ECMAScript 5 开始,全局变量 undefined 是不可写的(non-writable),即使试图赋值,它仍然是 undefined。
附加补充
1、在 URL 和 JSX 中的应用
在 HTML <a> 标签的 href 中:
<a href="javascript:void(0)">点击无跳转</a>
可以防止页面因 href="javascript:..." 返回非 undefined 值而跳转新内容。
在 React/JSX 中避免页面刷新:
<a href="#" onClick={e => { e.preventDefault(); /* ... */ }}>
链接
</a>
// 或
<a href="javascript:void(0)">链接</a>
2、与 IIFE(立即调用函数表达式)的结合
早期:为了在不将函数定义解析为声明的前提下,直接执行匿名函数。
void function(){
console.log('IIFE');
}();