JavaScript中有两个特殊的值,undefined与null。日常在写项目时,遇到须要判断一个值是否为空的时候,我总会想到undefined与null。既然都是表明空值(之前我就是这么认为的),那么它们有没有区别呢?反正我是一直傻傻分不清楚,看了又忘了。最近查阅了一些资料,才发现本身之前真的是误解它们了,知错就改,下面总结一下它们的用法和区别。css
在介绍undefined与null以前,咱们先来了解一下ECMAScript中的数据类型。在ECMAScript中有五种简单数据类型(也称为基本数据类型): Undefined、Null、Boolean、Number 和 String 。还有一种复杂数据类型——Object。express
没错,首字母大写的Undefined与Null其实都属于ECMAScript中的基本数据类型。这两个数据类型是五种数据类型中最特殊的两个类型,由于它们都只有惟一的一个值,分别是undefined与null,就是咱们今天要介绍的两个主角。函数
上面咱们说过了,Undefined类型只有一个值,就是特殊的undefined,在两种状况下咱们会获得undefined:post
声明了一个变量,但未对其初始化时,这个变量的值就是undefined。学习
var data;
console.log(data === undefined); //true
复制代码
那么我么是否能够显式地把一个变量初始化为undefined呢,答案是能够的。测试
var data = undefined;
console.log(data === undefined); //true
var value = 1;
console.log(data); //1
value = undefined;
console.log(data === undefined); // true
复制代码
通常而言,咱们不存在须要显式地把一个变量设置为undefined值的状况,由于对于未经初始化的值默认就会取得undefined值,而已经初始化的值再将其赋值为undefined来表示空值是没有意义且不可取的。何况字面值undefined的主要目的以用于比较,来区分空对象指针(后面咱们会介绍到这指的就是null)与未经初始化的变量的状况。ui
对未定义的变量执行typeof操做符也会返回undefinedlua
//data变量未定义
var value;
console.log(typeof data); // "undefined"
console.log(typeof value); // "undefined"
复制代码
这里咱们没有使用===
来判断,由于对于还没有声明过的变量,咱们只能执行一项操做,即便用typeof操做符检测其数据类型,使用其余的操做都会报错。spa
//data变量未定义
console.log(data === undefined); //报错
复制代码
结果代表对未初始化和未声明的变量执行typeof操做符都返回了undefined值,这个结果有其逻辑上的合理性。由于虽然这两种变量从技术角度看有本质区别,但实际上不管对哪一种变量也不可能执行真正的操做。设计
还有其余几种状况也会返回undefined,好比一个函数若是没有使用return语句指定返回值,就会返回一个undefined值,或者调用函数时没有传参数值,参数一样也会被初始化为undefined值。这些都是属于上面两种状况在代码中的体现,这里就不单独解释了。
从上面的例子咱们能够看出,不管咱们是否初始化过变量,均可以给变量赋值为undefined。其实这里用于赋值的undefined不是一个值,它是一个属性名,undefined是全局对象的一个属性,也就是说,它是全局做用域的一个变量,即window.undefined
,而window.undefined
这个属性的值才是前面所说的原始值undefined。data = undefined;
这就至关于把一个变量window.undefined
的值赋值给另外一个变量data
,这个值就是原始值undefined。其实在JavaScript代码中,咱们看到的undefined大多数状况指的都是window.undefined
(本篇文章中多数状况下也是,原始值undefined除外),原始值undefined多数状况下只存在于文档或规范中,不存在于JavaScript代码中(具体能够理解为代码中参与判断、比较或赋值的都是window.undefined
,而在控制台中输出,或函数中返回的则是原始值undefined)。
console.log(window.undefined); //原始值undefined
复制代码
注意,在ES3以前实际上是没有原始值undefined这个值的,第三版引入这个值,实际上是为了正式区分空对象指针(后面咱们会介绍到这指的就是null)与未经初始化的变量。在ES3中,window.undefined
就是一个普通的属性,咱们彻底能够把它的值改成任何真值。但从ES5以后,window.undefined
成了一个不可写,不可配置的数据属性,它的值永远是undefined。
你们可能注意到了,上面我提到的是在大多数状况下undefined指的都是window.undefined
,那还有什么其余状况吗?其实在ECMAScript中,undefined不是一个保留字,这意味着什么呢?也就是说咱们能够将undefined做为一个局部变量来使用,就像局部做用域中任何其余普通变量同样,没有任何特殊性,咱们能够对其赋予任何类型的值。
(function() {
var undefined = 'not is undefined';
console.log(undefined); //"not is undefined"
console.log(typeof undefined) // "string"
})()
复制代码
咱们能够看到undefined的值和类型都已经改变,这样的作法是很是不友好的,这样会使咱们的代码难以维护和排错。
如何判断一个变量是否为undefined,这里有两种方法。
使用严格相等符===
或不相等操做符!==
来决定一个变量是否拥有值,这里不使用标准相等操做符==
,是由于标准相等符还会会检查变量是否是为null,可是严格相等操做符不会检查。null不等同于undefined,这点咱们会在后面讲到。
使用typeof操做符,这种方式咱们在上面已经使用过了,对未定义的变量检测时只能使用这种方式,要否则会出现报错。
上面咱们提到过了,undefined做为局部变量使用是能够被重写的,那么若是咱们使用下面这种判断方式,是有风险的。
if(data === undefined){
//do something
}
复制代码
那么咱们怎样作才能确保万无一失呢?让咱们先来了解一下void运算符,官方文档是这样解释的:
The void operator evaluates the given expression and then returns undefined.
void 运算符 对给定的表达式进行求值,而后返回 undefined
什么意思呢?就是使用void对后面的表达式求值,不管结果是多少,都会返回原始值undefined。所以咱们能够用void 0
来代替undefined进行判断,由于void 0
始终返回的都是原始值undefined。
var data;
console.log(data === void 0); //true
复制代码
Null类型是第二个只有一个值的数据类型,这个特殊的值就是null。值 null 是一个字面量,它不像undefined 是全局对象的一个属性。从逻辑角度来看,null值表示一个空对象指针,指示变量未指向任何对象。把 null 做为还没有建立的对象,也许更好理解。在 APIs 中,null 常在返回类型是对象,但没关联值的地方使用,就像下面一个例子。
//document.getElementById() 能够返回对拥有指定 ID 的第一个对象的引用
var $container = document.getElementById("container"); // 注意:container是不存在的
console.log($container); // null
复制代码
当咱们使用typeof操做符检测null值,咱们理所应当地认为应该返"Null"类型呀,可是事实返回的类型倒是"object"。
var data = null;
console.log(typeof data); // "object"
复制代码
是否是很奇怪?其实咱们能够从两方面来理解这个结果
一方面从逻辑角度来看,null值表示一个空对象指针,它表明的其实就是一个空对象,因此使用typeof操做符检测时返回"object"也是能够理解的。
另外一方面,其实在JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。因为 null 表明的是空指针(大多数平台下值为 0x00),所以,null的类型标签也成为了 0,typeof null就错误的返回了"object"。在ES6中,当时曾经有提案为历史平凡, 将type null的值纠正为null, 但最后提案被拒了,因此仍是保持"object"类型。
null的判断可使用严格相等符===
或不相等操做符!==
判断,不使用标准相等符的缘由是由于undefined会影响判断结果。和undefined不同,不能使用typeof来判断一个值是否为null,缘由上边已经讲了,使用typeof来检测null会返回"object",这样的话咱们是没办法判断的。
if(data === null){
console.log("data中没有保存对象引用!");
}
复制代码
那么咱们在什么状况下须要将变量赋值为null呢?这里我想到的有两种状况。
若是定义的变量在未来用于保存对象,那么最好将该变量初始化为null,而不是其余值。换句话说,只要意在保存对象的变量尚未真正保存对象,就应该明确地让该变量保存null值,这样有助于进一步区分null和undefined。
当一个数据再也不须要使用时,咱们最好经过将其值设置为null来释放其引用,这个作法叫作解除引用。不过解除一个值的引用并不意味着自动回收改值所占用的内存。解除引用的真正做用是让值脱离执行环境,以便垃圾收集器在下次运行时将其回收。解除引用还有助于消除有可能出现的循环引用的状况。这一作法适用于大多数全局变量和全局对象的属性,局部变量会在它们离开执行环境时(函数执行完时)自动被解除引用。
实际上undefined值是派生自null值的,所以ECMA-262规定对它们的相等性测试要返回true:
console.log(null == undefined); //true
复制代码
由于使用的是标准相等符==
,这个操做符出于目的会转换其操做数为相同类型后再作比较,若是咱们使用严格相等符比较,咱们会发现它们是不相等的,由于严格相等符不会进行类型转换,然而undefined与null属于不一样的类型,因此不相等。
console.log(null === undefined); //false
复制代码
尽管null和undefined有这样的关系,但上面咱们已经提到过了,它们的用途彻底不一样,咱们在日常使用时必定要学会区分。
《JavaScript高级程序设计(第三版)》
JavaScript中undefined和null的区别
JavaScript 参考文档 null
JavaScript 参考文档 undefined
JavaScript 参考文档 typeof
(void 0) 与 undefined 之间的小九九
花了一些时间来总结undefined与null的用法和区别,之前一直没有注意,通过此次总结才发现它们所表明的的意义和用法彻底不一样。可能在日常使用的时候咱们不须要考虑这么多问题,但经过从新看一些东西,总结一些东西,经过这样的方式对我来讲收获挺大的。本篇文章纯属我的的学习总结,若是文章中存在错误的地方,但愿你们能够向我指出。
本篇文章发表在我的博客CavsZhouyou's Blog上,欢迎你们参观!