我准备写一个JavaScript的系列,这个系列并不是一个对JavaScript进行全面系统地描述的教程,而是选取这门语言中的某些重要的点,结合我自己的经验和思考进行解读,尽量让大家有茅塞顿开的感觉。初步计划包括下面的内容:数据类型、函数和构造器、JS的Main函数、事件队列、作用域链和上下文、闭包、原型和继承等。另外,整个系列只讨论ECMA-262第六版之前的内容。
每种语言都有自己的数据类型,JavaScript有六种数据类型。包括五种基本类型:Undefined、Null、Boolean、String和Number,以及Object。
1. 原始类型
在JS中,除了Object外其他所有类型的值都是不可变的(immutable)。
(1) Undefined类型
Undefined类型只有一种取值:undefined。 当一个变量还没有被赋值时,它的值就是undefined。
var book; console.log(book); // 输出undefined
[注]:相当于C语言中一个变量刚被声明,还没有被赋值,取值不定,是不可用的。
(2) Null类型
Null类型只有一种取值:null。
var car = null;
[注]:null用来表示“不存在的对象”。例如某个函数的返回值是对象类型,但是在某些情况下函数无法获取对象或对象不存在,就可以返回null来代替。
(3) 布尔类型
Boolean类型代表一个逻辑实体,它只有两种取值:true和false。
var rainy = true; var hot = false;
(4) 字符串类型
字符串类型是由零个或多个16位无符号的整型值(元素)组成的所有有序序列的集合。字符串类型通常用来表示程序中的文本数据,每个元素用UTF-16编码存储。字符串中的每个元素都占据一个位置,这个位置被称为索引。
字符串是不可变的,但是JS提供了很多基于一个字符串来创建一个新的字符串的方法。例如:
var lang = 'JavaScript'; var lang2 = lang.substr(0, 4); // lang2的值是'Java' var lang3 = lang.replace('Java', 'VB') // lang3的值是'VBScript'
(5) 数字类型
根据ECMAScript标准,JS中只有一种数字类型:基于IEEE754的双精度64位二进制格式的值,它的取值范围为-(253-1)~253-1。
var age = 28; var temperature = 36.5;
并不存在专门的整数类型,例如3和3.0是等价的。
除了可以表示浮点数外,数字类型还有三种符号类型的值:+Infinity(即Infinity)、-Infinity和NaN(非数)。
其中,+Infinity和-Infinity用来表示+∞和-∞;NaN意为非数(Not a Number)。例如:
1/0; // 输出Infinity -1/0; // 输出-Infinity 0/0; // 输出NaN
可以用 Number.isFinite() 判断一个数是否是有限的,用 Number.isNaN() 判断一个数是否是非数。需要注意的是,Number.isFinite方法与全局的isFinite方法不同,对于不是数字类型的参数,前者不会将其转为数字类型,所以返回值总是false;而后者会先将其转为数字类型。Number.isNaN方法与全局的isNaN相比,也有类似的区别。所以我们应当使用Number中的方法代替全局的同名函数,以避免可能的问题发生。
Number.isFinite('1'); // 输出false isFinite('1'); // 输出true
对于数字类型的值,使Number.isFinite()返回false的值包括+Infinity、-Infinity和NaN,使Number.isNaN()返回false的值只有NaN。
另外,需要注意的是,在全局作用域和Number中实际上都定义了NaN,即NaN和Number.NaN,它们是等价的。通常,我们在程序中不会直接使用NaN,在数学函数执行出错时或者将其他类型的值转为数字类型出错时才会产生NaN。
2. 对象类型
对象是存储在内存中的一块区域,它可以通过一个标识符来引用。可以通过多种方法创建一个JS对象:
var obj = new Object(); // 通过new操作符和构造函数创建对象 var person = {name: 'Lucy', age: 25}; // 通过对象字面量语法创建对象
[注]:上面的变量obj并不是对象本身,它只是指向内存中的对象,可以认为它是一个“引用”或“指针”。
在JS中,对象可以看作若干属性的集合,即键值对。对象有两种类型的属性:数据属性(Data Property)和访问器属性(Accessor Property)。
[注]:键值对中的键(即属性的名称)是一个字符串,包括空字符串。在使用 obj[p] 存取对象的属性时,p首先会被转为字符串。同样地,在使用 for(i in arr) 遍历数组时,i是字符串类型的,而不是数字类型的。
(1) 数据属性
数据属性是将键与一个值、一组布尔值结合起来。它具有以下性质:
[[Value]]、[[Writable]]、[[Enumerable]]、[[Configurable]]
(2) 访问器属性
访问器属性是将键与一个或两个访问器函数(get和set)、一组布尔值结合起来。访问器函数用来获取或存储这个属性的值。它具有以下性质:
[[Get]]、[[Set]]、[[Enumerable]]、[[Configurable]]
(3) 内部属性
内部属性没有名字,而且无法直接访问,是实现语言规范时使用的。
3. 类型检测
给定一个变量,如何判断它的类型呢?使用typeof操作符。下表(来自ECMA-262 Edition 5.1)是typeof操作各种数据类型的结果:
操作数的类型 | 结果 |
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
Object (原生的,且没有实现[[call]]的) | "object" |
Object (原生的或宿主的,且实现了[[call]]的) | "function" |
Object (宿主的,且没有实现[[call]]的) |
由实现定义,但不能是"undefined"、"boolean"、"number"或"string" |
[注]:
1. 对于null,使用typeof是有问题的,可以认为是JS本身的缺陷;
2. 所谓[[call]],是ECMAScript规范里面的说法。简单地说,“实现了[[call]]”的对象就是指函数(函数是一种对象),是可以被调用的。从上表可以看出:typeof操作普通的对象返回"object";操作所有函数对象返回"function";操作由宿主环境(例如浏览器)定义的对象返回的结果不确定,但不能是"undefined"、"boolean"、"number"或"string"。
举例如下:
// 输出"undefined" typeof undefined; // 输出"object",这个比较特殊 typeof null; // 以下均输出"boolean" typeof true; typeof false; // 以下均输出"string" typeof ''; typeof 'Tom & Jerry'; // 以下均输出"number" typeof 123; typeof 3.14; typeof 0xFE; typeof Infinity; typeof NaN; // 输出"object" typeof {a: 1, b: false}; // 以下均输出"function" typeof function() {console.log(123);} typeof Object; typeof String;
另外,使用instanceof操作符(例如 val instanceof A )可以判断A是否是val的类型或父类型。关于这一点后面再讲。
4. 类型转换
待续...