在学习javascript类型之前,我们可以先来看下几个问题?
- 为什么有的编程规范要求用void 0 代替undefined?
- 字符串有最大长度吗?
- 0.1 + 0.2 不是等于0.3吗? 为什么javascript里不是这样的?
- ES6新加入的Symbol是什么?
- 为什么给对象添加的方法能用在基本类型上?
如果你回答的有些犹豫,那么说明你对这部分知识点还有一些遗漏的地方。那就请往下看。
在研究js的知识体系的时候,我们可以从运行时,文法,执行过程三个角度去剖析,对于类型系统,我们从运行时的角度去分析。
运行时类型是代码实际执行过程中我们用到的类型,所有的类型数据都会属于7个类型之一。从变量、参数、返回值到表达式中间结果,任何javascript代码
类型
javascript语言的每一个值都有相应的数据类型,而javascript规定了7种语言类型,广泛用于函数参数,返回值,表达式等场合,根据最新的语言标准,这7种语言类型分别是
- Undefined
- Null
- Boolean
- String
- Number
- Symbol
- Object
其中Symbol类型是新的类型。后面的文章会进行解释。
Undefined、Null
我们的第一个问题,为什么有的编程规范要求用void 0 代替undefined?现在我们就分别看下。
大多数计算机语言有且仅有一个表示"无"的值,比如C语言的NULL等,但是javascript却有两个表示"无"的值,undefined和null,这是为什么呢?让我们先来了解一下它们。
相似性
在语法上两者差别不大,当我们执行下面的语句的时候
if(!undefined)
console.log('undefined is false');
// undefined is false
if(!null)
console.log('null is false')
// null is false
undefined == null
// true
undefined === null
// false
上面的是隐式转换的结果,虽然===揭示了两者并不同,但是在某种程度上来讲,两者的差别很小,几乎可以看成是同一个值,那么为什么会设计这两个类型与值呢?让我们先来了解下Undefined和Null这两种类型。
Undefined
Undefined类型只有一个值undefined,表示一个表示“无”的原始值,转为数值时为NaN,任何变量在赋值之前都是Undefined类型,值为undefined。下面的代码可以说明一下
var a;
console.log(a); // undefined
undefined是全局对象的一个属性,也就是说他是全局作用域的一个变量,可以被更改。
而在现代浏览器(JavaScript 1.8.5/Firefox 4+),自ECMAscript5标准以来undefined是一个不能被配置(non-configurable),不能被重写(non-writable)的属性。但是在之前的老版本浏览器中undefined依然只是一个变量而不是关键字,而且在非全局作用域中,他也是一个变量,如下代码。
(function() {
var undefined = 'foo';
console.log(undefined, typeof undefined)
})()
// foo string
所以undefined这个值是可以像变量进行修改的。
而void运算符可以运算它的运算式后返回undefined,无法被其更改,所以一些编程规范会建议使用void 0来得到undefined,避免无意的修改。
Null
Null类型也只有一个值,就是null,他的语义表示空值,转为数值时为0; 与undefined不同,而null是javascript关键字,可以放心使用。
设计缘由
这段设计缘由来自阮一峰博客。
在《Speaking JavaScript》中提到了这个缘由。
原来在javascript诞生之初,像java一样只设置了null作为表示“无”的值。
而根据c语言的传统,null 被设计成可以自动转为0。
Number(null)
// 0
5 + null
// 5
但是,JavaScript的设计者Brendan Eich,觉得这样做还不够,有两个原因。
首先,null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,也就是上面提到的7种类型中的前6种(原始类型)和最后一种Object(合成类型),Brendan Eich觉得表示"无"的值最好不是对象。
其次,JavaScript的最初版本没有包括错误处理机制,发生数据类型不匹配时,往往是自动转换类型或者默默地失败。Brendan Eich觉得,如果null自动转为0,很不容易发现错误。
因此,Brendan Eich又设计了一个undefined。
最初设计区别
null是一个表示“无”的对象,转为数值时为0;
undefined是一个表示"无"的原始值,转为数值时为NaN;
Number(null)
// 0
Number(undefined)
// NaN
现在的区别
null是表示缺少的标识,指示变量未指向任何对象,即此处不该有值。
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
// foo不存在,他从来没有被定义或者是初始化过;
foo;
"ReferenceError: foo is not defined"
// foo 现在已经是已知存在的,但是它没有类型或者是值:
var foo = null;
foo;
null
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。以下是使用场景
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
深入与抽象理解
讲到这里你应该还有困惑,让我们深入底层理解。
由于null表示的是一种人为重置为空对象的对象,在内存中的表现就是栈中的变量没有指向堆中的内存对象。
在js引擎的使用中,如果我们把一个对象置为null。那么引擎会在下一次回收的时候将其回收。那如果我们使用undefined是不是会感到明显的不合适?
如果不理解栈堆模型的的可以去看下我的另一篇关于变量类型的文章。
现在再思考下这个问题,大多数计算机语言有且仅有一个表示"无"的值,比如C语言的NULL等,但是javascript却有两个表示"无"的值,undefined和null,这是为什么?
这与javascript的动态性有着联系,在javascript中,成员除了表示存在的空值外,还有可能根本不存在,具体的存在需要在运行时才知道。而静态语言根本不需要考虑这个问题,编译阶段就会检查出错,所以存在一种状态(undefined)来表示对某个成员的getter是取不到的。
几个问题
为什么typeof null === ‘object’
null 有属于自己的类型Null,二不属于Object类型,typeof之所以会判定为Object类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。
000 - 对象,数据是对象的应用
1 - 整型,数据是31位带符号整数
010 - 双精度类型,数据是双精度数字
100 - 字符串,数据是字符串
110 - 布尔类型,数据是布尔值
如何获取null的真实类型?
Object.prototype.toString.call(null); // [object Null]
其他的数据类型也是可以的,原理我会在后续文章中写出来。
事实上,type null 结果是"object"更像是设计失误。比如说JSON格式中,一个成员定义为特定类型(string),也可以设置其值为null来表示这个值是空值。并不表示这个成员为空对象。wiki上在 harmony 中有提议将这个返回值修正为 null https://editor.csdn.net/md/?articleId=105342845。
但是会引起大量旧javascript脚本出现问题而否决了。
为什么设置一个值为 undefined 是不合理的
null 表示一个值被定义了,定义为“空值”;
undefined 表示根本不存在定义。
所以设置一个值为 null 是合理的,如objA.valueA = null;
但设置一个值为 undefined 是不合理的,如objA.valueA = undefined;
应该直接使用 delete objA.valueA; 任何一个存在引用的变量值为undefined都是一件错误的事情。
这样判断一个值是否存在,就可以用objA.valueA === undefined 。不应使用 null 因为 undefined == null,而 null 表示该值定义为空值。
总结
null 表示一个值被定义了,定义为“空值”;
undefined 表示根本不存在定义。
虽然看上去undefined和null很简单,但是深究下来其实是有很多的有趣的地方。是js语言中很重要的一部分。也希望各位能够看懂。
参考资料如下:
阮一峰博客- 关于undefined与null的区别
极客时间-重学前端
博客园一像素博文
stack overflow
ESDiscuss
MDN - undefined
MDN-null