黄子涵

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);

image
image

3.9.2 从数值转换为字符串值

// 通常的做法是使用 String 函数,
// 或是在对数值对象进行了隐式数据类型转换之后,
// 再对其调用 toString方法。
console.log("将数值100转换为字符串值'100':");
console.log(typeof String(100));
console.log("将数值100转换为字符串值'100'。");
console.log("为了区分小数点和点运算而必须使用括号:");
console.log(typeof (100).toString());
console.log("将数值hzh1转换为字符串值:");
var hzh1 = 100;
console.log(String(100));
console.log("将数值hzh1转换为字符串值:");
console.log(hzh1.toString());
console.log("***********************************************");
// 在字符串运算的操作数位置上写数值的话,
// 就将对其进行隐式数据类型转换。
// + 运算符的操作数中如果含有字符串,
// 则会作为字符串连接运算符使用。
console.log("数值100被转换为了字符串值'100':");
console.log('foo' + 100);
console.log("就算数值位于左操作数的位置也一样会被转换为字符串值:");
console.log(100 + 'foo');
console.log("将数值hzh1转换为字符串值:");
var hzh1 = 100;
console.log('foo' + hzh1);

image

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

可以通过多种方式来实现数据类型转换。具体哪种方法的运行速度更快和具体码的运行速度,还应当尽可能地缩短源代码的长度。只有缩短代码长度才能够减少网络传输的时间。因此,以较短的代码长度实现数据类型转换的写法成为了首选。

// 最为简短的数据类型转换写法.虽然使用String(n)或Number(s),
// 代码可读性可能会更高一些,不过这样的写法能够实现更好的性能
// 从数值转换为字符串值
var hzh1 = 3;
console.log("将数值3转换为字符串值'3'(利用了字符串连接运算符):");
console.log(hzh1 + '');
// 从字符串值转换为数值
var hzh2 = '3';
console.log("将字符串值'3'转换为了数值3(利用了正号运算):");
console.log( + hzh2);

image

隐式数据类型转换虽然简单,但容易引发错误。最为典型的错误就是在将字符串值转换为数值类型时,其结果可能会是 NaN。

一旦在运算中出现了 NaN,整个数值运算的结果都将变为 NaN。这样不仅无法得到正确的结算结果,而
且无法通过逆向的数据类型转换来得到原来的字符串。

在字符串值和数值之间进行数据类型转换时需要注意的地方

image

3.9.4 转换为布尔型

在实际编程中,从其他类型向布尔型的数据类型转换是很重要的。在 if 语句或是 while 语句等的条件表达式中,会有很多这样的隐式数据类型转换。

  • 数值0
  • 数值NaN
  • null值
  • undefined值
  • 字符串值""(空字符串值)
// 下面代码中在if语句的条件表达式写了数值0
// 在经过隐式数据类型转换之后,它将变为布尔型的false。
if(0) {
    console.log('黄子涵说0是真的');
}
else {
    console.log('黄子涵说0是假的');
}
console.log("*******************************************");
// 尽管也可以通过 Boolean 函数,将一个值显式地转换为布尔型。
// 使用 !! 来进行隐式的数据类型转换的。
// ! 运算是用于布尔型操作数的逻辑非运算。
// 在操作数不是布尔型的情况下会自动将其转换为布尔型。
// 因此只要使用 !! 这样的双重否定,就能够将值转换为布尔型。
console.log("输出(!!1)的结果:");
console.log(!!1);
console.log("输出(!!x)的结果:");
console.log(!!'x');
console.log("输出(!!0)的结果:");
console.log(!!0);
console.log("输出(!!'')的结果:");
console.log(!!null);
console.log("*********************************************");
// 在进行布尔型的数据类型转换时,应当对 Object 类型的情况多加注意。
// Object 类型在被转换为布尔型之后结果必定为 true。
var hzh1 = new Boolean(false);
if(hzh1) {
    console.log("黄子涵说hzh1是真的");
}
else {
    console.log("黄子涵说hzh1是假的");
}
console.log("");
var hzh2 = new Number(0);
if(hzh2) {
    console.log("黄子涵说hzh2是真的");
}
else {
    console.log("黄子涵说hzh2是假的");
}
console.log("");
var hzh3 = new String('');
if(hzh3) {
    console.log("黄子涵说hzh3是真的");
}
else {
    console.log("黄子涵说hzh3是假的");
}
console.log("*********************************************");
// 像下面这样,通过函数调用方式获得的结果就不再是 Object类型,
// 而是相应的内建类型了。
var hzh4 = Boolean(false);
if(hzh4) {
    console.log("黄子涵说hzh4是真的");
}
else {
    console.log("黄子涵说hzh4是假的");
}
console.log("");
var hzh5 = Number(0);
if(hzh5) {
    console.log("黄子涵说hzh5是真的");
}
else {
    console.log("黄子涵说hzh5是假的");
}
console.log("");
var hzh6 = String('');
if(hzh6) {
    console.log("黄子涵说hzh5是真的");
}
else {
    console.log("黄子涵说hzh5是假的");
}

image

3.9.5 其他的数据类型转换

对布尔值、null 值和 undefined 值进行数据类型转换的情况

image

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

对 Object 类型进行数据类型转换

image
image

// 将 Object 类型分别显式地与隐式地转换为字符串型
// 生成空对象
var hzh1 = {};
console.log("将对象隐式地转换为字符串型(通过字符串连接运算符):");
console.log(hzh1 + '');
console.log("将对象显式地转换为字符串型:");
console.log(String(hzh1));
console.log("*************************************************");
// 上面的结果是调用了对象 obj 的 toString 方法而得到的。
// 如果像下面这样,改变了 toString 方法的实现的话,
// 结果也将随之发生变化
console.log("确认当前toString方法的结果:");
console.log(hzh1.toString());
// 重写toString方法的实现
hzh1.toString = function() {
    return '黄子涵';
}
console.log("确认toString方法的结果:");
console.log(hzh1.toString());
console.log("将对象隐式地转换为字符串型:");
console.log(hzh1 + '');
console.log("将对象显式地转换为字符串型:");
console.log(String(hzh1));

image

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

转换为 Object 类型

image

和基本数据类型之间的类型转换一样,Object 类型和基本数据类型之间的数据类型转换,也可以根据上下文语境隐式地进行。因此,实际编程中,并不太需要进行显式的数据类型转换,反倒是更应该注意避免由于隐式数据类型转换而引发的错误。

// 下面的代码并不会报错,而是会将变量hzh1的值转换为NaN
var hzh1 = {};
console.log("该对象被隐式地转换为了数值型:");
console.log(hzh1++);
console.log("变量hzh1的值为NaN:");
console.log(hzh1);

image

如果一个对象没有能返回恰当数值的 valueOf 方法,或是能返回可以被转换为恰当数值的字符串的toString 方法的话,它在被转换为数值型之后的结果将是 NaN。又因为对 NaN 进行 ++运算的结果为 NaN,所以变量 obj 的最终值将是 NaN。

专栏

JavaScript 的性能分析工具

使用性能分析工具,就能了解一个运行中的 JavaScript 程序正在执行哪一个函数、正在执行哪一行,以及花费了多少时间。使用性能分析工具的目的通常是为了检查程序中运行速度较慢的部分。这样的部分被称为运行瓶颈。根据经验,如果程序的运行很慢,很有可能是由于其中某一部分花费了较多的运行时间。因此,如果提高了瓶颈部分的执行速度,程序整体的运行性能也很可能得以提高。反过来说,如果不确定程序的瓶颈所在,就胡乱地进行性能优化,效果往往差强人意。
程序的执行时间并不仅仅由一些代码的处理时间决定。对于客户端 JavaScript 来说,以下几个因素相结合最终决定了用户的实际使用感受。

  • JavaScript 代码的执行时间(这也是仅凭性能分析工具所能测得的)
  • DOM 的渲染时间
  • 网络响应时间
    Firebug、IE 的开发者工具和 Chrome 的开发者工具都具有
    基本的性能分析功能。此外,表 A 还列举了一些包含了其
    他各方面的分析测试功能的性能分析工具
性能分析工具

image

posted @ 2022-05-26 09:07  黄子涵  阅读(28)  评论(0编辑  收藏  举报