undefined 和 null
与其他的一些主流语言不同,在 JS 中,代表“无”的概念有两个值:null 和 undefined。但在含义上这两个值是有差异的:null 代表变量的值为“空”,而 undefined 代表变量已声明但未初始化。通俗地讲,null 表明变量已赋值,只是值为“空”;undefined 表明变量还未赋值。
为什么需要两个代表“无”的值?简单地说,这是个历史遗留问题。
最初,Brendan Eich 设计 JS 时,只有 null 没有 undefined。但是,由于 typeof null 的结果是 ‘object’ —— 现在普遍认为这是 JS 的一个 bug —— 加上 JS 数据类型分为原始类型和对象类型,因此,Brendan Eich 觉得代表“无”的值最好不是对象以及其他一些原因,最终导致增加了 undefined。
由于是后加入的,所以 undefined 与 null 在实现与限制上有较大的不同。本质上说,null 是一个字面值,而 undefined 是一个全局变量——虽然,通常也把它视为字面值。
1 | // 浏览器中 |
对于 null 而言,它不是一个合法的标识符,因此,如果试图声明名为 null 的变量,会抛出异常。
1 | var null = 0; // SyntaxError: Unexpected token null |
但是,undefined 不同,它本身就是一个全局变量,因此,undefined 显然是一个合法标识符,因此,可以声明名为 undefined 的变量。
但这并不代表可以修改全局的 undefined,在最新的宿主环境中,它通常是只读的。
1 | // 大多数最新版浏览器中 |
undefined的只读特性是不确定的,尤其在老的浏览器版本中,它可能是可写的,比如 IE8。在 IE8 中执行代码
console.log(Object.getOwnPropertyDescriptor(window, undefined));会得到如下结果:{configurable: false, enumerable: false, value: undefined, writable: true}
然而,关键不在于全局的 undefined 是否只读,关键在于只要 undefined 是合法标识符,我们就可以声明名为 undefined 的局部变量,因而,在局部作用域中局部 undefined 变量将隐藏全局 undefined。
总之,形如 xxx === undefined 这样的判等是不可信的、有风险的。
最佳实践
要判断一个变量是否是 undefined,常见的是使用 typeof 操作符:
1 | typeof oneVar == 'undefined' |
但是,在某些情况下,我们的确需要使用 undefined 的某种可信表示,比如在 switch 语句中。这时惯例会使用 void 0 代替 undefined:
1 | switch (oneVar) { |
void 0 是什么鬼?
根据规范,void 是个一元操作符,无论其右侧的计算表达式是什么,结果都将返回值 undefined——这正是我们想要的结果。理论上说,我们可以用任意的 void xxx,使用 void 0 只是惯例。
void的另一个用法常见于页面中,用以产生一个“无效”值。比如:
<a href='javascript:void(0)'>或<img src='javascript:void(0)'>。