第3章 JavaScript的数据类型


3.1 数据类型的定义

数据类型决定了一个数据的特征,即限定了该数据必须按照一定的规则进行操作。在程序设计中也 是如此,特定数据类型的数据会有其相应的行为模式。

JavaScript 中有以下 5 种基本数据类型。

  • 字符串型
  • 数值型
  • 布尔型
  • null 型
  • undefined 型
    在这 5 种基本数据类型之外的都被称为 Object 类型。也就是说,总的来看,JavaScript 中的数据类型可以分为 6 种。

3.1.1 在数据类型方面与 Java 作比较

动态数据类型与静态数据类型

像 Java 这样,变量具有数据类型的语言,被称为静态数据类型语言;而像 JavaScript 这样,变量没有类型的语言,则被称为动态数据类型语言。

基于类与基于原型

对于 Java 来说,内建类型(int 或 double 之类)之外的都是用户自定义类型。用户自定义类型又可以分为类和接口两种类型。Java 的用户自定义类型的使用方法,从其名称中就可略知一二,即开发者需要书写该类型的定义语句来定义该类型。而对象则作为这些由用户定义的数据类型的实例(实体)存在。这就是 Java 的基本特性。这种编程风格被称为基于类的语言风格。

另一方面,在 JavaScript 的语言规范中,不存在定义数据类型的语句。不需要使用特别的语句就能定义一个对象的属性或方法,而这样也就决定了该对象的类型。所谓类型也就是行为方式上的共性。由于每个对象都具有共同的行为方式,所以可以使用原型对象。这样的编程风格被称为基于原型的风格。

3.1.2 基本数据类型和引用类型

虽然 JavaScript 的变量不具有数据类型,但从概念上,JavaScript 变量可以分为基本数据类型变量和引用类型变量。基本数据类型变量直接保存有数值等类型的数据的值,而引用类型变量则保存有对象的引用。尽管表面上两者没有区别,但其内在是不同的。因此为了正确地理解其内部实现原理,就需要引入引用这一概念.

3.2 内建数据类型概要

在 ECMAScript 标准中,内建数据类型(built-in type)分为 5 种基本数据类型以及 Object 类型。

JavaScript 的基本数据类型

avaScript 的字符串型是基本数据类型,而 Java 的字符串型并不是基本数据类型,这是两者的区别之一。不过,其实两者在本质上并没有太大的不同。因为在 Java 中,字符串型和字面量以及运算符一样,属于被特别对待的 Object 类型。字符串连接运算符(+ 号)在 Java 和 JavaScript 中的作用也是相同的。在 JavaScript 中,字符串值会被隐式地转换为字符串对象类型。熟悉 Java 的人很容易就能掌握JavaScript 中字符串型的用法。不过在 JavaScript 中不存在字符类型。如果需要表达某个字符的话,请使用长度为 1 的字符串值。

JavaScript 只有一种数值类的数据类型,其内部构造为 64 位的浮点小数,这相当于 Java 中的 double 类型。和 JavaScript的情况不同,在 Java 中有 5 种整数类型和 2 种浮点数类型。JavaScript 之所以只支持一种数值类型,是由于设计当初更多地着眼于降低编程难度,而非提升运行效率。如果有多种数值类型,就不得不在考虑在进行赋值时考虑是否会产生错误。而 JavaScript 数值只有一种数值数据类型,除了部分特殊情况,通常不会发生类型转换错误。从另一方面来说,由于数值类型能够与其他类型进行隐式转换,所以仍然存在大量陷阱。

布尔型在 Java 和 JavaScript 中是没有区别的,同样都是使用 true 和 false 的字面量。

null 类型的值只有 null 一种情况,并且 null 属于字面量。虽然 Java 中也存在 null 这一字面量,但是没有 null 类型,null只是一种可以被引用的值而已。尽管有这样的差别,但是 Java 中的 null 和 JavaScript 中的 null 在用法上几乎没有区别,只需要注意一下类型转换的问题即可。

undefined 类型是指未定义的值的类型,这一概念在 Java 中是不存在的。如果在 Java 中使用类似于 undefined 类型的值的话,就会发生编译错误。

3.3 字符串型

3.3.1 字符串字面量

// 字符串值可以通过字符串字面量来表示。
// 字符串字面量需要用双引号(")或单引号(')括起来。
var hzh = "hzh";  // 将字符串"hzh"值赋给变量hzh
console.log("hzh:" + hzh); // 显示变量hzh的值

var hzh = 'hzh'; // 将字符串"hzh"值赋给变量hzh
console.log("hzh:" + hzh); // 显示变量hzh的值

特殊字符可以通过转义字符(串)来表示。可以通过在转义符之后使用特定字符,来表达一些特殊的含义。转义符是反斜杠(\)。例如,\n 是换行符的表达方式。

// 因为可以用两种引号来包围字符串字面量
// 所以只要对此加以活用,就能够少用转义字符。
// 例如,在包含大量双引号的字符串字面量外使用单引号,
// 就能够减少转义字符的出现。
// 对于双引号不需要使用转义字符(当然转义字符也没有问题)
var hzh1 = '我说"黄子涵是帅哥!"';
console.log("hzh1 = " + hzh1);

3.3.2 字符串型的运算

// 如果在右侧书写的是值为字符串值的变量,也一样能将其值赋值给等号左侧的变量。
var hzh1 = '黄子涵';             // 将字符串值'黄子涵'赋值给变量hzh
var hzh2 = hzh1;                // 将变量hzh1赋值给变量hzh2
console.log("hzh1:" + hzh1);   // 输出变量hzh1的值
console.log("hzh2:" + hzh2);   // 变量hzh2的值为字符串'黄子涵'
console.log("**************************************************");

console.log("**************************************************");
// 不过 JavaScript 确实和Java 一样,其字符串型是不可变类型。
var hzh3 = "黄子涵";
var hzh4 = "是帅哥!"
var hzh5 = hzh3 + hzh4;         // 连接字符串值
console.log("hzh3:" + hzh3);   // 变量hzh5的值为字符串值“黄子涵是帅哥!”
console.log("hzh4:" + hzh4);
console.log("hzh5:" + hzh5);
console.log("**************************************************");

console.log("**************************************************");
// += 运算符可以在连接字符串的同时进行赋值。
var hzh6 = "黄子涵";
hzh6 += "是靓仔!"
console.log("hzh6:" + hzh6);  // 变量hzh6的值为字符串值“黄子涵是靓仔”
console.log("**************************************************");

console.log("**************************************************");
var hzh7 = "黄子涵";
var hzh8 = hzh7;
hzh7 += "真厉害!"             // 变量hzh7的值为字符串值“黄子涵真厉害”
console.log("hzh7:" + hzh7);
console.log("hzh8:" + hzh8); // 变量hzh8的值仍保持为字符串值“黄子涵”
console.log("**************************************************");

console.log("**************************************************");
// 可以通过typeof运算符来获知值的数据类型
console.log(typeof "黄子涵");            // 对字符串字面量进行typeof运算

var hzh9 = "黄子涵";
console.log(typeof hzh9);               // 对变量hzh9进行typeof运算
console.log(typeof (hzh9));             // 也可以添加括号
console.log(typeof (typeof (hzh9)));    // typeof运算的结果也是字符串值

3.3.3 字符串型的比较

JavaScript 有两种等值运算符,即 =====。与之对应,也有两种不等运算符 !==!=

=== 和 == 的区别在于,在比较的时候是否会进行数据类型的转换。=== 在比较的时候不会对数据类型进行转换。在ECMAScript 标准中,将其称为严格相等(strict equal)。

// 如果只考虑字符串之间的比较,=== 和 == 的结果是没有区别的。
// 两种方式都会判断字符串的内容是否一致。
var hzh1 = "黄子涵";
var hzh2 = "是帅哥!";
var hzh3 = hzh1 + "是帅哥!";
console.log("hzh1 == hzh2?" + (hzh1 == hzh2));
console.log("hzh1 == hzh3?" + (hzh1 == hzh3));
console.log("hzh2 == hzh3?" + (hzh2 == hzh3));
console.log("hzh1 != hzh2?" + (hzh1 != hzh2));
console.log("hzh1 != hzh3?" + (hzh1 != hzh3));
console.log("hzh2 != hzh3?" + (hzh2 != hzh3));
console.log("**************************************************");
//对字符串值的比较基于 Unicode 字符的编码值(编码位置)。
var hzh4 = "hzh";
var hzh5 = "HZH";
console.log("hzh4 < hzh5?" + (hzh4 < hzh5));
console.log("hzh4 <= hzh5?" + (hzh4 <= hzh5));
console.log("hzh4 > hzh5?" + (hzh4 > hzh5));
console.log("hzh4 >= hzh5?" + (hzh4 >= hzh5));

下面是比较大小时 Unicode 的编码位置的一些典型情况。要深入理解这部分的内容,需要有 Unicode 的相关知识,不过只要能记住以下的情况,就多少能进行运用了。

  • 英文字母是字典顺序(ABC 顺序)
  • 英文的大写字母在小写字母之前
  • 数字和符号在英文字母之前(不过有些符号是在英文字母之后的)
  • 日文的平假名在片假名之前
  • 日文的平假名和片假名都是字典顺序(あいうえお顺序)
  • 日文浊音和半浊音的顺序则是按以下的规律排列:へ、ほ、ぼ、ぽ、ま
  • 日文汉字在平假名和片假名之后
  • 日文汉字的排列顺序视计算机的具体情况而定(有些是按照音读的字典顺序)
    在实际中,只有英语单词

3.3.4 字符串类(String 类)

之前提到,在 JavaScript 中字符串型是一种内建类型。不过 JavaScript 的字符串也有容易使人混淆的地方,即除了内建类型的字符串之外还存在一个字符串类。

字符串类的名称为 String。JavaScript 中字符串型和 String 类的关系,大致相当于 Java 中数值型和包装类型(Number 类和 Integer 类)的关系。字符串型和 String 类之间也同样支持隐式类型转换。在 Java 中存在装箱和拆箱转换,在 JavaScript 的字符串型和 String 类之间也有着类似的转换。这
一转换通常是隐式进行的。

var hzh1 = '黄子涵是帅哥!'
// 在形式上类似于读取字符串值的属性
console.log("数组hzh1的长度:" + hzh1.length);
// 在形式上类似于读取字符串字面量的属性
console.log("'黄子涵是帅哥!'的长度:" + '黄子涵是帅哥!'.length);
// 上述代码,其内部发生了字符串值到String对象的隐式数据类型转换

3.3.5 字符串对象

// 可以使用new运算符,来显示地生成一个字符串对象
var hzh1 = new String('黄子涵');  // 生成字符串对象
console.log("输出hzh1:" + hzh1);
console.log("hzh1的类型:" + (typeof hzh1));
console.log("*****************************************");
// 隐式类型转换也能反向进行
// 将字符串对象隐式转换为了字符串值
var hzh2 = hzh1 + '是帅哥!';
console.log("输出hzh2:" + hzh2);
console.log("hzh2的类型:" + (typeof hzh2));
console.log("*****************************************");
// 对象的相等运算,判断的是两者是否引用了同一对象
// 而并非两者的内容相同
var hzh3 = new String('黄子涵');
var hzh4 = new String('黄子涵');
// 虽然字符串的内容相同,但是并非引用了同一个对象,所以结果是false
console.log("判断hzh3和hzh4是否相等:");
console.log(hzh3 == hzh4);
// 虽然字符串的内容相同,但是并非引用了同一个对象,所以结果是false
console.log("判断hzh3和hzh4是否严格相等:");
console.log(hzh3 === hzh4);
console.log("*****************************************");
// 对于字符串值和字符串对象的等值判断,
// 如果使用的是会进行隐式数据类型转换的 == 运算,
// 则只会判定其内容是否相同,如果内容相同则结果为真。
var hzh5 = new String('黄子涵是帅哥!');
var hzh6 = '黄子涵是帅哥!';
// 进行数据类型转换的等值运算的结果为true
console.log("判断hzh5和hzh6是否相等:");
console.log(hzh5 == hzh6);
// 不进行数据类型转换的等值运算的结果为false
console.log("判断hzh5和hzh6是否严格相等:");
console.log(hzh5 === hzh6);
// 对于比较大小运算,字符串对象和字符串值一样,都是比较其字符串内容。
// 因此可以认为,这时字符串值和字符串对象之间没有区别。

3.3.6 避免混用字符串值和字符串对象

// 可以使用 typeof 运算来判别一个字符串是字符串值还是字符串对象。
// 字符串对象的 typeof 运算结果为"object"。
var hzh1 = new String('黄子涵是帅哥!');
console.log("判断hzh1的数据类型:");
console.log(typeof hzh1);
console.log("*********************************************");
// 应该避免显式地生成字符串对象。
// 需要使用字符串值的时候,一般都使用字符串字面量。
// 对于其余的情况,通过 String 函数进行显式的数据类型转换就足够了。
// 应该是积极地使用隐式数据类型变换,将字符串值转换为字符串对象。
// 转换为字符串对象后,只要在字符串值之后写上点运算符和属性名,
// 就能对字符串进行各种各样的操作了。
var hzh2 = '黄子涵是帅哥!'
console.log("返回字符串值下标为1的字符:");
console.log(hzh2.charAt(1));
console.log("对于字符串字面量也能像这样进行方法调用:");
console.log('黄子涵是帅哥!'.charAt(1));

3.3.7 调用 String 函数

// 通过调用 String 函数就可以生成字符串值。
// 一般来说,使用 String 函数是为了进行显式的数据类型转换。
var hzh1 = String('黄子涵是帅哥!');
// 变量hzh1的值是字符串型
console.log("判断hzh1的数据类型:");
console.log(typeof hzh1);
// 由数值类型向字符串值类型的显式数据类型变换
var hzh2 = String(47);
console.log("输出hzh2的值:");
console.log(hzh2);
// 变量hzh2的值是字符串型
console.log("判断变量hzh2的数据类型:");
console.log(typeof hzh2);

3.3.8 String 类的功能

String 类的函数以及构造函数调用

String 类的属性

String.prototype 对象所具有的属性

String 类的实例属性

// 通过数值属性获取指定下标的字符
// 其返回值是一个 String对象
var hzh1 = new String('黄子涵');
console.log("输出下标1的字符:");
console.log(hzh1[1]);
// 由于有隐式数据类型转换,
// 所以对字符串值也能进行这样的操作
console.log("字符串值'黄子涵'下标为2的字符:");
console.log('黄子涵'[2]);

3.3.9 非破坏性的方法

// 字符串对象和字符串值一样,是不可变的。
// 也就是说,不能改写字符串的内容。
// 所有要改变字符串内容的方法,都会生成一个新的字符串对象然后将其返回。
var hzh1 = new String('hzh');
// 调用对象hzh1的toUpperCase方法
var hzh2 = hzh1.toUpperCase();  
// 对象hzh1的内容不会发生变化
console.log("输出hzh1的值:");
console.log(hzh1);
console.log("输出hzh2的值:");
console.log(hzh2);
// 即使是 JavaScript 独有的 [] 运算也不会改写字符串的内容
hzh1[0] = 'HZH';
console.log("输出hzh1的值:");
console.log(hzh1);
// 改变内部状态的方法被称为破坏性的方法。
// 一般来说,非破坏性的方法更好一些。
// 不过在有些时候,非破坏性方法的效率会相对较低。

3.4 数值型

3.4.1 数值字面量

在 JavaScript 中,数值的内部结构为 64 位的浮点小数。不过在实际的编程中,使用整数的情况会更多。不管内部构造如何,从 JavaScript 的代码上来看,只要是写为整数就能够作为整数使用,而不必考虑是否是浮点数的问题。因为所有的数值都是浮点小数,所以其运行效率多少会有些下降。因此对于比较注重运行效率的程序来说,JavaScript 可能并不是合适的选择。

由于整数型和浮点数型在使用上没有差别,所以不会发生和类型转换相关的错误。当然,浮点小数本身所具有的缺点依然存在。不过对于大部分现代程序设计语言来说,如果要使用小数,这些问题还是难以避免。

// 下面是使用数值字面量的代码示例
// 将数值1赋值给变量hzh1
var hzh1 = 1;
// 将数值2赋值给变量hzh2
var hzh2 = 2;
// 将变量hzh1的值域变量hzh2的值相加
console.log("输出hzh1和hzh2的和:");
console.log(hzh1 + hzh2);
console.log("*********************************************");
// 可以通过 typeof 运算符来判断数值的类型。
// 对于数值来说,typeof 运算符返回的结果是字符串值 "number"。
var hzh3 = 1;
// 对数值进行typeof运算
console.log("输出hzh3的数据类型:");
console.log(typeof hzh3);
// 对数值字面量进行typeof运算
console.log("输出1的数据类型:");
console.log(typeof 1);

3.4.2 数值型的运算

对于数值可以进行 +(加法)、-(减法)、*(乘法)、/(除法)四则运算。通过 % 符号则可以进行求模运算(即计算除法运算后的余数)。

需要注意的是,尽管从代码上来看进行的是整数运算,但其实在其内部进行的仍然是浮点数运算。例如,对 0 作除法并不会得到错误的结果,而是会得到一个特殊的数值。

var hzh1 = 1;
var hzh2 = 2;
console.log("输出1除以0的结果:");
console.log(hzh1/0);
console.log("输出2除以0的结果:");
console.log(hzh2/0);

在 JavaScript 标准对象中有一个 Math 对象。该对象定义了圆周率 PI、自然对数的底数 E 等数学常量,以及一些相关的数学函数。例如,可以通过 Math.pow 函数计算 2 的 10 次方。

var hzh1 = Math;
console.log("打印圆周率的e");
console.log(hzh1.E);
console.log("打印圆周率的PI");
console.log(hzh1.PI);
console.log("计算2的10次方:");
console.log(hzh1.pow(2,10));

3.4.3 有关浮点数的常见注意事项

// 对于浮点数来说,有时候并不能正确地表达小数点以后的部分。
// 实际上,能够正确表达一个数的值反而是一种例外,
// 大部分情况下浮点数只能表达数值的近似值
console.log("计算0.1和0.2的和:");
console.log(0.1 + 0.2);
console.log("判断0.1和0.2的和是否与0.3相等?");
console.log((0.1 + 0.2) == 0.3);
console.log("判断0.1和0.2的和是否与0.3严格相等?");
console.log((0.1 + 0.2) === 0.3);
console.log("输出1/3的值:");
console.log(1/3);
console.log("输出(10/3)-3的值:");
console.log((10/3)-3);
console.log("判断((10/3)-3)是否与1/3相等?");
console.log(((10/3)-3) == (1/3));
console.log("判断((10/3)-3)是否与1/3严格相等?");
console.log(((10/3)-3) === (1/3));

对于浮点数来说,要执行正确的实数运算是不可能的。稍加注意就会发现,这并不是个应该如何避免的问题,而是从原理上就是无法回避的。对于整数的情况,则可以保证在 53 位的范围内能有正确的结果。因此,如果只是使用 53 位以内的整数的话,就不会有任何问题。

如果需要用到数值正确的实数,就必须使用类似于 Java 中的 BigDecimal 类的实数库。目前,JavaScript 并没有标准的实数库。不过也有特殊情况,如果是在 JVM 下使用 Rhino(一种基
于 Java 的开源 JavaScript 实现)的话,就能够直接使用 Java的 BigDecimal 类了。

3.4.4 数值类(Number 类)

正如存在字符串类(String 类),JavaScript 中也存在数值类(Number 类)。字符串值和字符串类在经过隐式数据类型转换之后,就基本能够以相同的方式使用,与此类似,经过数据类型转换之后,数值和数值对象也能被视为等价的。

// 为了区分小数点和点运算符而必须使用括号
var hzh1 = (1).toString();
console.log("输出(1).toString()的值:");
console.log(hzh1);
// 确认是否确实是从数值转换为了字符串
console.log("判断(1).toString()的数据类型:");
console.log(typeof hzh1);
console.log("***************************************************");
// 由于会进行隐式数据类型转换,因此数值和数值对象在外表上是没有什么区别的
// 如有必要,可以通过 typeof 运算对其进行判断
// 对数值对象执行 typeof 运算的结果为 "object"
var hzh2 = new Number(1);
console.log("判断Number(1)的数据类型:");
console.log(typeof hzh2);

3.4.5 调用 Number 函数

// 和 String 函数类似,以通常的方式调用 Number 函数的话,将返回相应的数值。
// 在需要显式地进行数据类型转换的时候,可以使用 Number 函数。
var hzh1 = Number(1);
// 变量hzh1的值为数值
console.log("判断Number(1)的数据类型:");
console.log(typeof hzh1);
console.log("判断hzh1和1是否相等:");
console.log(hzh1 == 1);
console.log("判断hzh1和1是否严格相等:");
console.log(hzh1 === 1);
console.log("*****************************************************");
// 从字符串值至数值型的显式数据类型转换
var hzh2 = Number('1');
console.log("输出Number('1')的结果:");
console.log(hzh2);
console.log("*****************************************************");
// 如果参数无法被转换为数值类型,Number 函数返回的结果将是 NaN
var hzh3 = Number('x');
console.log("输出Number('x')的结果:");
console.log(hzh3);
console.log("判断Number('x')的数据类型:");
console.log(typeof hzh3);
console.log("*****************************************************");
var hzh4 = new Number('x');
console.log("输出new Number('x')的结果:");
console.log(hzh4);
console.log("判断new Number('x')的数据类型:");
console.log(typeof hzh4);

3.4.6 Number 类的功能

Number 类的函数以及构造函数调用

Number 类的属性

Number.prototype 对象的属性

Number 类的实例属性

3.4.7 边界值与特殊数值

// 可以通过 Number 对象的属性值来获知 64 位浮点数所支持的最大正值和最小正值
// 如果在其之前添加负号(运算符),就能够获得相应的最大负值和最小负值
var hzh1 = Number.MAX_VALUE;
var hzh2 = Number.MIN_VALUE;
var hzh3 = - Number.MAX_VALUE;
var hzh4 = - Number.MIN_VALUE;
console.log("输出Number.MAX_VALUE的结果:");
console.log(hzh1);
console.log("输出Number.MIN_VALUE的结果:");
console.log(hzh2);
console.log("输出- Number.MAX_VALUE的结果:");
console.log(hzh3);
console.log("输出- Number.MIN_VALUE的结果");
console.log(hzh4);
console.log("*******************************************************");
// 还可以通过 Number.MAX_VALUE.toString(16) 来获得相应的 16 进制数值
var hzh5 = Number.MAX_VALUE.toString(16);
console.log("输出Number.MAX_VALUE.toString(16)的结果:");
console.log(hzh5);

浮点小数的特殊数值

// 在 JavaScript 中,浮点数的内部结构遵循 IEEE754 标准。
// 可以通过 Number 对象的属性值来获得在 IEEE754 中定义的一些特殊数值。
var hzh6 = Number.POSITIVE_INFINITY;
var hzh7 = Number.NEGATIVE_INFINITY;
var hzh8 = Number.NaN;
console.log("输出Number.POSITIVE_INFINITY的结果:");
console.log(hzh6);
console.log("输出Number.NEGATIVE_INFINITY的结果:");
console.log(hzh7);
console.log("输出Number.NaN的结果");
console.log(hzh8);

从内部结构来看,这 3 个特殊数值(即正负无穷大与 NaN)都是基于 IEEE754 标准的比特位数值。虽然它们在形式上属于数值(typeof 运算符对它们的执行结果为 number),但是并不能作为数值进行计算。例如,将最大正数值乘以 2 之后能够得到正无穷大,但反之则不成立,正无穷大除以 2 之后无法得到最大正数值。

事实上,这 3 个特殊数值对于任何运算都无法得到通常的数值结果。

var hzh1 =  Number.MAX_VALUE * 2;
console.log("输出hzh1的结果:");
console.log(hzh1);   // 将最大正数值乘以2之后能够得到正无穷大
console.log("输出(hzh1/2)的结果:");
console.log(hzh1/2); // 再除以2之后却无法得到原值
console.log("输出(hzh1*0)的结果:");
console.log(hzh1*0); // 即使乘以0,得到的结果也不是0

3.4.8 NaN

// 对 NaN 进行任何运算,其结果都是 NaN。
// 因此,如果在计算过程中出现了一次 NaN,
// 最终的结果就一定会是 NaN
console.log("输出(NaN + 1)的结果:");
console.log(NaN + 1);
console.log("输出(NaN*0)的结果:");
console.log(NaN * 0);
console.log("输出(NaN - NaN)的结果:");
console.log(NaN - NaN);
console.log("*****************************************");
// NaN 不但不与其他任何数值相等,
// 就算是两个 NaN 的等值判断,其结果也为假
console.log("输出(NaN == 1)的结果:");
console.log(NaN == 1);
console.log("输出(NaN === 1)的结果:");
console.log(NaN === 1);
console.log("输出(NaN == NaN)的结果:");
console.log(NaN == NaN);
console.log("输出(NaN === NaN)的结果:");
console.log(NaN === NaN);
console.log("*****************************************");
console.log("输出(NaN > 1)的结果:");
console.log(NaN > 1);
console.log("输出(NaN >= 1)的结果:");
console.log(NaN >= 1);
console.log("输出(NaN > NaN)的结果:");
console.log(NaN > NaN);
console.log("输出(NaN >= NaN)的结果:");
console.log(NaN >= NaN);

// JavaScript 中预定义了一个全局函数 isNaN。
// isNaN 函数的返回值为真的条件是其参数的值为 NaN,
// 或是在经过数据类型转换至数值类型后值为 NaN。
console.log("使用isNaN判断NaN:");
console.log(isNaN(NaN));
// NaN值的Number对象
var hzh1 = new Number(NaN);
console.log("判断hzh1的数据类型:");
console.log(typeof hzh1);
console.log("使用isNaN判断hzh1:");
console.log(isNaN(hzh1));
console.log("使用isNaN判断{}:");
console.log(isNaN({}));
console.log("*****************************************");
// 而预定义全局函数 isFinite 可以对 3 个特殊数值
// (即 NaN与正负无穷大)之外的数值进行判断
console.log("使用isFinite判断数值1:");
console.log(isFinite(1));
console.log("使用isFinite判断数值NaN:");
console.log(isFinite(NaN));
console.log("使用isFinite判断数值Infinity:");
console.log(isFinite(Infinity));
console.log("使用isFinite判断数值-Infinity:");
console.log(isFinite(-Infinity));

可以得到浮点数特殊数值的运算

// 在 ECMAScript 第 5 版中,已将 NaN 和 Infinity 改为了只读变量,
// 因此不能再对它们进行数值更改。
// 需要注意的是,在对它们进行赋值的过程中并不会报错。
console.log("输出NaN:");
NaN = 7;
console.log(NaN);
console.log("输出Infinity:");
Infinity = 8; 
console.log(Infinity);

3.5 布尔型

3.5.1 布尔值

// 布尔型也被称为逻辑值类型或者真假值类型。
// 布尔型只能够取真(true)和假(false)两种数值。
// 除此以外,其他的值都不被支持。
var hzh1 = true;
console.log("它们是true还是false?");
console.log("hzh1?" + hzh1);
console.log("(!hzh1)?" + (!hzh1));     // true的否定为false
console.log("(!!hzh1)?" + (!!hzh1));   // 否定的否定为肯定
console.log("**************************************************");
// 对布尔值进行 typeof 运算的话,得到的结果为 "Boolean"。
console.log("它们是什么数据类型?");
console.log("true?" + true);
console.log("false?" + false);
console.log("hzh1?" + hzh1);
console.log("(!hzh1)?" + (!hzh1));     // true的否定为false
console.log("(!!hzh1)?" + (!!hzh1));   // 否定的否定为肯定

3.5.2 布尔类(Boolean 类)

// 布尔类(Boolean 类)是一种布尔型的包装类型。
// 其地位以及使用方法和 String 类以及 Number 类相同。
// 像下面这样对布尔值使用点运算符的话,
// 就能够对布尔值进行隐式数据类型转换,将其转为布尔对象。
// 但是,布尔类中并没有什么实用的方法,所以基本上也很少会去使用
console.log("隐式数据类型转换为了Boolean对象:");
console.log(true.toString());

3.5.3 Boolean 类的功能

Boolean 类的函数以及构造函数调用

Boolean 类的属性

Boolean.prototype 对象的属性

Boolean 类的实例属性

3.6 null型

null 值的意义存在于对象引用之中。null 值最初的含义为“没有引用任何对象”。null 型只能够取 null 这一个值。null 值是一个字面量。由于只支持 null 这个值,所以将 null 型称为一种类型未免有些奇怪。不过从语法规则上来看,null 型确实是一种数据类型。

然而,对 null 值进行 typeof 运算得到的结果也是 "object"(具体原因尚不得知)。因此,尽管其他的基本数据类型都可以通过 typeof 运算来进行类型判断,但对于 null 型来说,就必须通过和 null 值的等值判断才能确定其类型。

console.log("typeof运算的结果为'object':");
console.log(typeof null);
// null 型没有与之相对应的 Null 类。
console.log("对null值进行点运算,就会产生TypeError异常:");
console.log(null.toString());
// 和其他程序设计语言一样,null 值可能引发各种各样的错误,
// 其中大部分和数据类型转换以及一些运算有关。

3.7 undefined型

// undefined 型只能够取 undefined 这一个值。
console.log("对undefined值进行typeof运算,其结果为undefined:");
console.log(typeof undefined);
// 从代码上来看,undefined 值似乎和 null 值一样都是一种字面量。
// 但实际上,它并非字面量,而是一个预定义的全局变量
console.log("对名称为 undefined 的全局变量进行赋值:");
undefined = '黄子涵'
console.log(undefined);
console.log("判断undefined的数据类型:");
console.log(typeof undefined);

undefined 值

全局变量 undefined 与 undefined 值的关系其实就是这样的:首先有了 undefined 型的值,之后才将该值赋值给了全局变量 undefined。

3.8 Object类型

3.9 数据类型转换

JavaScript 这种语言很容易在进行数据类型转换时发生错误。因为不具有强数据类型,所以会有大量的隐式数据类型转换。JavaScript 会根据上下文语境,自动地进行数据类型转换。例如,无论对 if 条件语句使用怎样的值,该值都将被转换为布尔型。

语句中所写的值也会被转换为和运算符相对应的值。例如,某个值与字符串值和连接运算符(+ 号)相连的话,不管该值是哪种类型,它都将被自动转换为字符串型。

这样的隐式数据类型转换,虽然有不需要进行显式的数据类型转换,以及不会产生类型不一致错误的优点,但是也有不足,且其中很多错误只有在运行时才能被发现。

不过,要是由于过分担心隐式数据类型转换可能造成的问题,而始终使用显式的数据类型转换的话,又不符合 JavaScript 编程的风格。JavaScript 注重的是灵活运用隐式数据类型转换,以写出简洁的代码。虽然确实存在着不少陷阱,但在必要的时候,应灵活使用显式的数据类型转换。

3.9.1 从字符串值转换为数值

通常的做法是使用 Number 函数、parseInt 函数和 parseFloat 函数。Number 函数的书写最为简单,不过需要注意的是,对于像 '100x' 这样的包含非数字的字符串值,函数返回的结果将是 NaN。而 parseInt 和 parseFloat 将会忽略数字以外的其他字符,所以 '100x' 将被转换为 100。parseInt 函数还可以通过第二参数来指定转换时所采用的基数(radix)。如果省略该参数则默认进行 10 进制转换。

console.log("将字符串值'100'转换为数值100:");
console.log(typeof Number('100'));
console.log("将字符串值'100x'转换为数值100:");
console.log(Number('100x'));
console.log("输出parseInt('100')的结果:");
console.log(parseInt('100'));
console.log("输出parseInt('100x')的结果:");
console.log(parseInt('100x'));
console.log("输出parseInt('x')的结果:");
console.log(parseInt('x'));
console.log("输出parseInt('ff', 16)的结果:");
console.log(parseInt('ff', 16));
console.log("输出parseInt('0xff', 16)的结果:");
console.log(parseInt('0xff', 16));
console.log("输出parseInt('ff', 10)的结果:");
console.log(parseInt('ff', 10));
console.log("输出parseInt('0.1')的结果:");
console.log(parseInt('0.1'));
console.log("输出parseFloat('0.1')的结果:");
console.log(parseFloat('0.1'));
console.log("*********************************************");
// 只要在数值运算操作数的位置上书写字符串值,
// 该值就将被隐式地转换为数值类型。
console.log("字符串值'100'被转换为了数值100:");
console.log('100' - 1);
console.log("两个操作数位置的字符串值被转换为了数值:");
console.log('100' - '1');
console.log("字符串转换为数值的惯用方式:");
console.log('100' - '');
console.log("*********************************************");
// 如果是加法运算,则不一定能获得期望的结果。
// 这是因为对于 + 运算来说,如果操作数中含有字符串值,
// 它就将变为字符串连接运算。
// 于是发生的就不再是从字符串值到数值的数据类型转换,
// 而是从数值到字符串值的数据类型转换了。
console.log("字符串连接运算:");
console.log('100' + 1);
console.log("即使第一个操作数是数值,也是字符串连接运算:");
console.log(1 + '100');
console.log("*********************************************");
// + 运算在作为单目运算符的情况下则是正号运算。
// 这时操作数将被转换为数值类型。
// 不过由于正号运算没有任何实质意义,
// 所以其作用就仅仅是将字符串值转换为数值。
console.log("将字符串值'100'转换为数值100:");
console.log(typeof + '100');
console.log("将字符串值'100'转换为数值100:");
var hzh1 = '100';
console.log(typeof + hzh1);


3.9.2 从数值转换为字符串值

3.9.3 数据类型转换的惯用方法

3.9.4 转换为布尔型

3.9.5 其他的数据类型转换

3.9.6 从 Object 类型转换为基本数据类型

3.9.7 从基本数据类型转换为 Object 类型

相关