ECMAScript基础之类型
类型与值
值的具体的概念,可以理解为是具体的某一个数据值,其表示的是某个具体的数据。true,"","test",1,2等都是一个具体的值。
类型是一个抽象的概念,本文中类型的定义为数据值的集合。举个例子true,false这两个值组成了Boolean类型;undefined这个值组成了Undefined类型。
原始类型
ES中变量是没有类型的,只有值有类型,所以接下来我们讨论的均是与值相关的问题,和变量没有什么关系。在ES规范中规定了六种原始值,其分别为:
- Undefined
- Null
- Boolean
- Number
- Symbol -- 在ES6中被加入
- String
ES中对这六种类型的定义分别为
Undeifined类型
值只有undefined值的类型,undefined值是在代码中直接书写undefined这几个字母,不带引号的("undefined")的一个值。
console.log(undefined in window); // true
undefined不是保留字,在浏览器的中是一个全局变量,这意味着undefined存在被重新赋值的可能性。这种可能性针对的是老旧的浏览器,由于这一原因我们会在某些库的源码中看到使用void 0来代替undefined,实现了ES5相关标准的浏览器undefined不会被重重新赋值。
Null类型
值只有null值的类型,null值也是直接书写在代码中的这几个字母组成的值。
Boolean类型
由原始值true和false组成的类型,如果某个值是Boolean类型,那个它一定是true或者false,不存在其他的可能。
String类型
由零个或多个16位无符号整数组成的有限有序序列原始值的集合
Number类型
所有可能的IEEE 754-2008 格式的 64 位双精度二进制的原始值的集合,NAN值,正无穷,负无穷
Symbol类型
表示一个唯一的,非字符串的属性键对象的原始值,其集合。
typeOf操作符
上述原始类型中五种可以使用typyof操作符准确识判断
console.log(typeof undefined === "undefined"); // true
console.log(typeof true === "boolean"); // true
console.log(typeof 42 === "number"); // true
console.log(typeof "42" === "string"); // true
console.log(typeof Symbol() === "symbol"); // true
typeof操作符可能返回的值有七种除了上面列出的五种还有"object"和"function",也就是说typeof操作符的返回值与原始类型不是一一对应的。
console.log(typeof null === "object");
console.log(typeof function() {} === "function");
以上代码在线示例https://jsbin.com/voroxam/3/edit?js,console
null值返回"object"其实是一处设计错误,而且在ES设计之初就一直存在,有太多的生产代码依赖这一特性,所以在后继的标准中也无法修复这个错误。
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了"object"。
原始类型的封箱与拆箱
原始类型除了undefined和null外均有其对应的对象,原始类型与其对象在代码实际上常常会相互转换的。考虑如下代码:
var a = "test";
var b = "testb"
var c = a.concat(b);
var d = a + b;
上述代码中c和d得到的是一样的结果,但是结果产生的方式却有所不同,得到c的过程将a在当前运算中转换为String实例,使其可调用String实例对象原型上的concat函数,而在得到d的过程是直接使用运算符+得到结果。
也就是说一个原始类型在代码中被当做对象的方式去调用的一些方法时,该原始类型会被包装成其对应的类型一个实例对象,该实例对象在需要被封箱的操作完成后就会被回收,从代码中我们无法获取也无需获取这个被包装的实例对象。
关于拆箱出现的场景,考虑如下代码:
var a = new String("test");
var b = new String("testb");
var c = a + b;
conosle.log(c);
var d = {name: "test"};
var e = {name: "teste"};
var f = d + e;
console.log(f);
在运行这段代码之前可以先考虑一个问题,对象和对象可以进行+运算么?
上述a+b和d+e均是两个对象的运算,此处将+两个的值强制转为原始类型字符串后进行的运算,至于此处是如何强制转换的,其转换的规则又是如何,可参考ES标准中的ToPrimitive算法。
这段封箱与拆箱操作只是有助于理解js代码的运行,实际应用意义不大,之所以记录这个问题是有人遇到过如下代码:
var a = 1;
a.b= function() {};
a.b();
考虑下上面代码的运行结果,其出现的原因又是什么?