JavaScript in-depth understanding of undefined and null

write in front

There are two special values ​​in JavaScript, undefined and null. Usually when writing a project, when I need to judge whether a value is empty, I always think of undefined and null. Since they both represent null values ​​(I thought so before), is there any difference between them? Anyway, I've been stupid and can't tell the difference, and I forgot after reading it. I recently checked some materials, only to find that I really misunderstood them before, and I corrected them when I knew the mistakes. The following summarizes their usage and differences.

basic data type

Before introducing undefined and null, let's take a look at data types in ECMAScript. There are five simple data types (also known as primitive data types) in ECMAScript: Undefined, Null, Boolean, Number, and String. There is also a complex data type - Object.

That's right, Undefined and Null in capital letters are actually basic data types in ECMAScript. These two data types are the most special of the five data types, because they both have only one value, undefined and null, which are the two protagonists we are going to introduce today.

Undefined type

definition

As we said above, the Undefined type has only one value, which is the special undefined, and we will get undefined in two cases:

  1. When a variable is declared but not initialized, the value of the variable is undefined.

    var data;
    console.log(data === undefined); //true
    

    So can we explicitly initialize a variable to undefined? The answer is yes.

    var data = undefined;
    console.log(data === undefined); //true
    
    var value = 1;
    console.log(data); //1
    
    value = undefined;
    console.log(data === undefined); // true
    

    In general, there is no need to explicitly set a variable to an undefined value, because an uninitialized value will get an undefined value by default, and an initialized value will be assigned to undefined to represent an empty value is meaningless and inadvisable. Moreover, the main purpose of the literal value undefined is for comparison, to distinguish between null object pointers (which we will introduce later refers to null) and uninitialized variables.

  2. Executing the typeof operator on an undefined variable also returns undefined

    //data变量未定义
    var value;
    
    console.log(typeof data); // "undefined"
    console.log(typeof value); // "undefined"
    

    这里我们没有使用===来判断,因为对于尚未声明过的变量,我们只能执行一项操作,即使用typeof操作符检测其数据类型,使用其他的操作都会报错。

    //data变量未定义
    
    console.log(data === undefined); //报错
    

    结果表明对未初始化和未声明的变量执行typeof操作符都返回了undefined值,这个结果有其逻辑上的合理性。因为虽然这两种变量从技术角度看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作。

还有其他几种情况也会返回undefined,比如一个函数如果没有使用return语句指定返回值,就会返回一个undefined值,或者调用函数时没有传参数值,参数同样也会被初始化为undefined值。这些都是属于上面两种情况在代码中的体现,这里就不单独解释了。

全局属性 window.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

大家可能注意到了,上面我提到的是在大多数情况下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 判断

如何判断一个变量是否为undefined,这里有两种方法。

  1. 使用严格相等符===或不相等操作符!==来决定一个变量是否拥有值,这里不使用标准相等操作符==,是因为标准相等符还会会检查变量是不是为null,但是严格相等操作符不会检查。null不等同于undefined,这点我们会在后面讲到。

  2. 使用typeof操作符,这种方式我们在上面已经使用过了,对未定义的变量检测时只能使用这种方式,要不然会出现报错。

void 0

上面我们提到过了,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。值 null 是一个字面量,它不像undefined 是全局对象的一个属性。从逻辑角度来看,null值表示一个空对象指针,指示变量未指向任何对象。把 null 作为尚未创建的对象,也许更好理解。在 APIs 中,null 常在返回类型是对象,但没关联值的地方使用,就像下面一个例子。

   //document.getElementById() 可以返回对拥有指定 ID 的第一个对象的引用

   var $container = document.getElementById("container"); // 注意:container是不存在的

   console.log($container); // null

typeof 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 判断

null的判断可以使用严格相等符===或不相等操作符!==判断,不使用标准相等符的原因是因为undefined会影响判断结果。和undefined不一样,不能使用typeof来判断一个值是否为null,原因上边已经讲了,使用typeof来检测null会返回"object",这样的话我们是没办法判断的。

   if(data === null){
       console.log("data中没有保存对象引用!");
   }

null 使用

那么我们在什么情况下需要将变量赋值为null呢?这里我想到的有两种情况。

  • 如果定义的变量在将来用于保存对象,那么最好将该变量初始化为null,而不是其他值。换句话说,只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null值,这样有助于进一步区分null和undefined。

  • 当一个数据不再需要使用时,我们最好通过将其值设置为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上,欢迎大家参观!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325344782&siteId=291194637