Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 之强制类型转换-笔记(你不知道的JavaScript中卷) #15

Open
heyushuo opened this issue Dec 27, 2019 · 0 comments
Open

Comments

@heyushuo
Copy link
Owner

强制类型转换

1.值类型转换

将值从一种类型转换为另一种类型通常称为类型转换, 在 JavaScript 中通常称为强制类型转换,本书为了跟好理解把强制类型转换分为两种隐式强制类型转换显式强制类型转换

// 例如:
var a = 42;
var b = a + ''; // "42" 隐式强制类型转换
var c = String(a); //"42" 显式强制类型转换

2.抽象值操作

ES5 规范中定义了一些“抽象操作”(即“仅供内部使用的操作”)和转换规则。(ToString``、ToNumberToBoolean)

2.1 ToString

抽象操作 ToString,它负责处理非字符串到字符串的强制类型转换。

var num = 666;
//极大数字
var infinityNum = 1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
var obj = {
  name: 'kebi',
  age: 36
};
var arr = [1, 2, 3];
//null和undefined没有toString() 方法
String(null); //"null"
String(undefined); //"undefined"
String(true); //"true"
num.toString(); //"666"
obj.toString(); //"[object Object]"
infinityNum.toString(); // "1.07e21"
arr.toString(); //1,2,3 数组的默认 toString() 方法经过了重新定义,将所有单元字符串化以后再用 "," 连接起来

2.2 ToNumber

有时我们需要将非数字值当作数字来使用,比如数学运算。为此 ES5 规范定义了抽象操作 ToNumber。

// 以下是Number处理基础类型的返回值
Number(false); //0
Number(null); //0
Number(true); //1
Number(undefined); //NAN

//
//
//
//

但是对于对象(包括数组) 会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型
,则再遵循以上规则将其强制转换为数字

  • 为了将值转换为相应的基本类型值, 会首先检查该值是否有 valueOf() 方法。如果有并且返回基本类型值, 就使用该值进行强制类型转换。
  • 如果没有就使用 toString()的返回值( 如果存在) 来进行强制类型转换。
  • 如果 valueOf()toString()均不返回基本类型值, 会产生TypeError错误。
注从 ES5 开始,使用 Object.create(null) 创建的对象 [[Prototype]] 属性为 null,并且没有 valueOf() 和 toString() 方法,因此无法进行强制类型转换。
var a = {
  valueOf: function() {
    return '42';
  }
};
var b = {
  toString: function() {
    return '42';
  }
};
var c = [4, 2];
c.toString = function() {
  return this.join(''); // "42"
};
Number(a); // 42
Number(b); // 42
Number(c); // 42
Number(''); // 0
Number([]); // 0
Number(['abc']); // NaN

2.3ToBoolean

JavaScript 中的值可以分为以下两类:

  • 可以被强制类型转换为 false 的值
  • 其他(被强制类型转换为 true 的值)
// 以下这些是假值:
Boolean(undefined);
Boolean(null);
Boolean(false);
Boolean(+0);
Boolean(-0);
Boolean(NaN);
Boolean('');
// 除了上边的以外都是真值
// 综上也可以看出以下所有值都是true
Boolean([]);
Boolean({});

3.显式强制类型转换

3.1 字符串和数字之间的显式转换

//第一种方式
var a = 42;
var b = String(a);
var c = '3.14';
var d = Number(c);
b; // "42"
d; // 3.14
// 第二种方式
var a = 42;
var b = a.toString();
var c = '3.14';
var d = +c;
b; // "42"
d; // 3.14
  • a.toString()是显式,不过其中涉及隐式转换。因为 toString() 对 42 这样的基本类型值不适用,所以 JavaScript 引擎会自动为 42 创建一个封装对象,然后对该对象调用 toString()
  • +c 是+ 运算符的一元( unary) 形式( 即只有一个操作数)。+ 运算符显式地将 c 转换为数字, 而非数字加法运算

日期显式转换为数字

var d = new Date('Mon, 18 Aug 2014 08:53:06 CDT');
+d; // 1408369986000
// 获取时间戳
var timestamp = +new Date();
// 一些常用的转换为时间戳
new Date().getTime();
Date.now();
我们不建议对日期类型使用强制类型转换,应该使用 Date.now() 来获得当前的时间戳,使用 new Date(..).getTime() 来获得指定时间的时间戳。

3.2 显式解析数字字符串

解析字符串中的数字和将字符串强制类型转换为数字的返回结果都是数字。但是解析强制类型转换两者有明显的区别

var a = '42';
var b = '42px';
Number(a); // 42
parseInt(a); // 42
Number(b); // NaN
parseInt(b); // 42

解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回 NaN

parseInt(..) 针对的是字符串值,非字符串参数会首先被强制类型转换为字符串,应该避免向 parseInt(..) 传递非字符串参数。

3.3 显式转换为布尔值

从非布尔值强制类型转换为布尔值的情况

// 从非布尔值强制类型转换为布尔值的情况
var a = '0';
var b = [];
var c = {};
var d = '';
var e = 0;
var f = null;
var g;
Boolean(a); // true
Boolean(b); // true
Boolean(c); // true
Boolean(d); // false
Boolean(e); // false
Boolean(f); // false
Boolean(g); // false
// 虽然 Boolean(..) 是显式的,但并不常用,常用如下写法
var a = '0';
var b = [];
var c = {};
var d = '';
var e = 0;
var f = null;
var g;
!!a; // true
!!b; // true
!!c; // true
!!d; // false
!!e; // false
!!f; // false
!!g; // false
在 if(..).. 这样的布尔值上下文中,如果没有使用 Boolean(..) 和 !!,就会自动隐式地进行 ToBoolean 转换。建议使用 Boolean(..) 和 !! 来进行显式转换以便让代码更清晰易读。

4.隐式强制类型转换

隐式强制类型转换的作用是减少冗余,让代码更简洁,同时会让代码变得晦涩难懂

4.1 字符串和数字之间的隐式强制类型转换

+ 运算符即能用于数字加法,也能用于字符串拼接。如果某个操作数是字符串或者能够通过抽象操作转换为字符串的话,+进行拼接操作。如果其中一个操作数是对象(包括数组),则首先对其调用抽象操作(toValue、toString)

var a = [1, 2]; //调用toString()得到  "1,2"
var b = [3, 4]; //调用toString()得到  "3,4"
var obj = {
  name: 'kebi'
}; //调用toString()得到  "[object Object]"
var num = 12;
var str = '12';
a + b; // "1,23,4"
a + obj; // "1,2[object Object]"
num + str; //"1212"
num + ''; //"12"

-减运算符

// 减运算符
var a = '3.14';
var b = a - 0;
b; // 3.14
// - 是数字减法运算符,因此 a - 0 会将 a 强制类型转换为数字。

var a = [3];
var b = [1];
a - b; // 2
//为了执行减法运算,a 和 b 都需要被转换为数字,它们首先被转换为字符串,然后再转换为数字。

4.2 布尔值到数字的隐式强制类型转换

true 为 1,false 为 0

4.3  隐式强制类型转换为布尔值

相对布尔值,数字和字符串操作中的隐式强制类型转换还算比较明显。下面的情况会发生布尔值隐式强制类型转换

  • if (..) 语句中的条件判断表达式。
  • for ( .. ; .. ; .. ) 语句中的条件判断表达式(第二个)。
  • while (..) 和 do..while(..) 循环中的条件判断表达式。
  • ? : 中的条件判断表达式。
  • 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。

非布尔值会被隐式强制类型转换为布尔值,例子如下

var a = 42;
var b = 'abc';
var c;
var d = null;
if (a) {
  console.log('yep'); // yep
}
while (c) {
  console.log('nope, never runs');
}
c = d ? a : b;
c; // "abc"
if ((a && d) || c) {
  console.log('yep'); // yep
}

4.4 逻辑运算符||和 &&

&&|| 运算符的返回值并不一定是布尔类型,而是两个操作数其中一个的值

var a = 42;
var b = 'abc';
var c = null;
a || b; // 42
a && b; // "abc"
c || b; // "abc"
c && b; // null
  • 对于||来说,如果条件判断结果为 true返回第一个操作数(a 和 c)的值,如果为 false 就返回第二个操作数(b)的值。
  • && 则相反,如果条件判断结果为 true 就返回第二个操作数(b)的值,如果为 false 就返 回第一个操作数(a 和 c)的值。

4.5 符号的强制类型转换

ES6 允许从符号到字符串的显式强制类型转换,然而隐式强制类型转换会产生错误。

var s1 = Symbol('cool');
String(s1); // "Symbol(cool)"
var s2 = Symbol('not cool');
s2 + ''; // TypeError

符号不能够被强制类型转换为数字显式和隐式都会产生错误),但可以被强制类型转换 为布尔值显式和隐式结果都是 true)。

5.宽松相等和严格相等

宽松相等(loose equals)== 和严格相等(strict equals)=== 都用来判断两个值是否“相
等”,常见的误区是“== 检查值是否相等,=== 检查值和类型是否相等”,正确的解释是:“== 允许在相等比较中进行强制类型转换,而 === 不允许。

  • 规定如果两个值的类型相同,就仅比较它们是否相等。例如,42 等于 42,"abc" 等于 "abc"。(注; NaN 不等于 NaN / +0 等于 -0)
  • 对象(包括函数和数组)的宽松相等 ==。两个对象指向同一个值时即视为相等,不发生强制类型转换。
  • == 在比较两个不同类型的值时会发生隐式强制类型转换,会将其中之一或两者都转换为相同的类型后再进行比较。

5.1 字符串和数字之间的相等比较

var a = 42;
var b = '42';
a === b; // false
a == b; // true

如果两个值的类型不同,则对其中之一或两者都进行强制类型转换,根据规范,"42" 应该被强制类型转换为数字以便进行相等比较

5.2 其他类型和布尔类型之间的相等比较

布尔类型会转换为 Number 类型 true 为 1,false 为 0

var a = '42';
var b = true;
a == b; // false  (1==42)

5.3 null 和 undefined 之间的相等比

null 和 undefined 之间的 == 也涉及隐式强制类型转换。

  • 如果 x 为 null,y 为 undefined,则结果为 true。
  • 如果 x 为 undefined,y 为 null,则结果为 true。

这也就是说在 ==null 和 undefined 是一回事,可以相互进行隐式强制类型转换。
a === undefined || a === null 效果和 a == null

5.4 对象和非对象之间的相等比较

关于对象(对象 / 函数 / 数组)和标量基本类型(字符串 / 数字 / 布尔值)之间的相等比教对象会调用抽象操作(valueOf 或者 toSring),布尔值会先转换为数字

var a = 42;
var b = [42];
a == b; // true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant