转 number 类型
Number 方法
- 对于 string:
(1)空字符串 ''
→ 0
(2)纯数字的字符串 → 数字
(可识别指数字符串)
(3)非纯数字的字符串 → 转不了,返回 NaN
console.log(Number('')); // 0
console.log(Number('123')); // 123 - 整数
console.log(Number('123.123')); // 123.123 - 浮点数
console.log(Number('1.5e5')); // 150000 - 指数
console.log(Number('123a')); // NaN
不仅可以转换 10 进制的数字字符串,还能转换 2 进制、8 进制、16 进制的数字字符串。
注意:8 进制的数字字符串必须是 0o
/ 0O
开头,0
开头会被认为是 10 进制的数字字符串。
// 2 进制
console.log(0b1010); // 10
console.log(Number('0b1010')); // 10
// 8 进制 - 0o 开头
console.log(0o12); // 10
console.log(Number('0o12')); // 10
// 8 进制 - 0 开头
console.log(012); // 10
console.log(Number('012')); // 12
// 16 进制
console.log(0xa); // 10
console.log(Number('0xa')); // 10
- 对于 boolean:
true
→1
;false
→0
console.log(Number(true)); // 1
console.log(Number(false)); // 0
- 对于 null:返回
0
console.log(Number(null)); // 0
- 对于 undefined:返回
NaN
console.log(Number(undefined)); // NaN
- 对于对象:会先检查该值是否有
valueOf()
方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用toString()
的返回值来进行强制类型转换。如果valueOf()
和toString()
均不返回基本类型值,会产生 TypeError 错误。
var a = {
valueOf: function () {
return '42';
},
};
Number(a); // 42
var b = {
toString: function () {
return '42';
},
};
Number(b); // 42
var c = [4, 2];
c.toString = function () {
return this.join(''); // "42"
};
Number(c); // 42
Number([]); // 0
Number([1]); // 1
Number([1, 2]); // NaN
Number(['abc']); // NaN
const date = new Date();
console.log(Number(date)); // 1408369986000
console.log(date.valueOf()); // 1408369986000
不建议对日期类型使用强制类型转换,应该使用 Date.now()
来获得当前的时间戳,使用 new Date(..).getTime()
来获得指定时间的时间戳。
注意:使用 Object.create(null)
创建的对象 [[Prototype]]
属性为 null
,并且没有 valueOf()
和 toString()
方法,因此无法进行强制类型转换。
综上,在基本类型数据中:
- 让
Number(xxx)
返回0
的值有:null
、false
、''
- 让
Number(xxx)
返回NaN
的值有:非纯数字的字符串
、undefined
正负号
本质上还是调用 Number()
,只是多了正负号。
console.log(-'123'); // -123
console.log(+''); // 0
console.log(-true); // -1
console.log(+'123.6a745'); // NaN
数学运算
var a = '3.14';
var b = a - 0;
b; // 3.14
-
是数字减法运算符,因此 a - 0
会将 a
强制类型转换为数字。也可以使用 a * 1
和 a / 1
,因为这两个运算符也只适用于数字,只不过这样的用法不太常见。
对象的 -
操作与 +
类似:
var a = [3];
var b = [1];
a - b; // 2
为了执行减法运算,a 和 b 都需要被转换为数字,它们首先被转换为字符串(通过 toString()
),然后再转换为数字。
转 string 类型
toString 方法
- 对于 number、boolean:
const num = 123;
console.log(num.toString()); // "123"
const bool = true;
console.log(bool.toString()); // "true"
对于 number,toString 能传入 1 个参数 x
(默认为 10),表示返回 x
进制的数字字符串:
const num = 10;
console.log(num.toString(2)); // "1010" —— 2 进制
console.log(num.toString(8)); // "12" —— 8 进制
console.log(num.toString(10)); // "10" —— 10 进制
console.log(num.toString(16)); // "a" —— 16 进制
对于极大/极小的 number,toString 会使用指数表示法:
const num = 1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
console.log(num.toString()); // "1.07e+21"
- 对于 null、undefined,没有包装类,不能调用 toString 方法,会报错:
const val = null;
console.log(val.toString()); // TypeError: Cannot read properties of null
const val = undefined;
console.log(val.toString()); // TypeError: Cannot read properties of undefined
- 对于普通对象,会调用
Object.prototype.toString()
,返回内部属性[[Class]]
的值[object Object]
:
const obj = {};
console.log(obj.toString()); // "[object Object]"
如果对象有自己的 toString 方法,字符串化时就会调用该方法并使用其返回值:
const obj = {
toString() {
return '123';
},
};
console.log(obj.toString()); // "123"
将对象强制类型转换为 string 是通过 ToPrimitive 抽象操作来完成的
- 对于数组,因为重新定义了 toString 方法,所以会调用
Array.prototype.toString()
,返回数组元素组成的字符串,元素之间用,
分隔:
const arr = [1, 2, 3];
console.log(arr.toString()); // "1,2,3"
String 方法
- 对于 number、boolean、普通对象、数组:其实就是调用 toString 方法。
- 对于 null、undefined:相当于直接用引号包裹。
console.log(String(undefined)); // "undefined"
console.log(String(null)); // "null"
字符串拼接
任意类型的数据与字符串拼接,都会被隐式转换成 string。
用空字符串进行拼接,效果与 String()
一样,且写起来更方便(推荐):
console.log('' + 123); // "123"
console.log('' + true); // "true"
console.log(undefined + ''); // "undefined"
console.log(null + ''); // "null"
也可以使用模板字符串:
console.log(`${123}`); // "123"
console.log(`${true}`); // "true"
console.log(`${undefined}`); // "undefined"
console.log(`${null}`); // "null"
对于对象:a + ""
和 String(a)
之间有一个细微的差别需要注意。根据 ToPrimitive 抽象操作规则,a + ""
会尝试调用 a.valueOf()
,然后再尝试调用 a.toString()
。而 String(a)
则是直接调用 a.toString()
。
var a = [1, 2];
var b = [3, 4];
a + b; // "1,23,4"
因为数组的 valueOf()
操作无法得到简单基本类型值,于是它转而调用 toString()
。因此上例中的两个数组变成了 "1,2"
和 "3,4"
。+
将它们拼接后返回 "1,23,4"
。
var a = {
valueOf: function () {
return 42;
},
toString: function () {
return 4;
},
};
a + ''; // "42"
String(a); // "4"
在定制 valueOf()
和 toString()
时需要特别小心,因为这会影响强制类型转换的结果。
有一个坑常常被提到,即
[] + {}
和{} + []
,它们返回不同的结果,分别是"[object Object]"
和0
。
转 boolean 类型
- 6 个负性值:
±0
、NaN
、''
、undefined
、null
、false
- 除了负性值,其他值转成 boolean 都是
true
Boolean 方法
- 对于 number:
0
&NaN
→false
;其他 →true
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(12)); // true
console.log(Boolean(Infinity)); // true
- 对于 string:
''
→false
;其他 →true
console.log(Boolean('')); // false
console.log(Boolean('a123')); // true
- 对于 null、undefined:
null
&undefined
→false
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
- 对于对象:
true
var a = []; // true
var b = {}; // true
var c = function () {}; // true
逻辑非
逻辑运算符 !
会把操作的数据转换为布尔类型,然后再取反。那再使用一次 !
,不就可以将当前数据转换为对应的布尔值了咯。
console.log(!!''); // false
console.log(!!'ok'); // true
隐式强制类型转换
下面的情况会发生布尔值隐式强制类型转换。
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
}
|| 和 &&
逻辑运算符 ||
(或)和 &&
(与)应该并不陌生。
我其实不太赞同将它们称为 “逻辑运算符”,因为这不太准确。称它们为 “选择器运算符”(selector operators)或者 “操作数选择器运算符”(operand selector operators)更恰当些。
为什么?因为和其他语言不同,在 JS 中它们返回的并不是布尔值。它们的返回值是两个操作数中的一个(且仅一个)。即选择两个操作数中的一个,然后返回它的值。
var a = 42;
var b = 'abc';
var c = null;
a || b; // 42
a && b; // "abc"
c || b; // "abc"
c && b; // null
||
和 &&
首先会对第一个操作数(a
和 c
)执行条件判断,如果其不是布尔值就先进行 ToBoolean 强制类型转换,然后再执行条件判断。
对于 ||
来说,如果条件判断结果为 true 就返回第一个操作数(a
和 c
)的值,如果为 false 就返回第二个操作数(b
)的值。
&&
则相反,如果条件判断结果为 true 就返回第二个操作数(b
)的值,如果为 false 就返回第一个操作数(a
和 c
)的值。
||
和 &&
返回它们其中一个操作数的值,而非条件判断的结果。c && b
中 c
为 null,是一个假值,因此 &&
表达式的结果是 null(即 c
的值),而非条件判断的结果 false。
a || b;
// 大致相当于:
a ? a : b;
a && b;
// 大致相当于:
a ? b : a;
之所以说 a || b
与 a ? a : b
大致相当,是因为它们返回结果虽然相同但是却有一个细微的差别。在 a ? a : b
中,如果 a
是一个复杂一些的表达式(比如有副作用的函数调用等),它有可能被执行两次(如果第一次结果为真)。而在 a || b
中 a
只执行一次,其结果用于条件判断和返回结果(如果适用的话)。a && b
和 a ? b : a
也是如此。
日常开发中经常会见到类似
a = b || "something"
和a && b()
这种 “短路” 机制。