导航
NaN
''
、' '
, '0'
、false
、null
、[]
、 等返回 0{}
、undefined
、NaN
被转为 NaN
NaN
parseInt
更宽松,false
, true
能转'1a'
, 'true'
这种字符串,会 NaNvar a = '123';
console.log(typeof(Number(a)) + '-' + Number(a)); // number-123
a = '3.14';
console.log(typeof(Number(a)) + '-' + Number(a)); // number-3.14
a = 'true';
console.log(typeof(Number(a)) + '-' + Number(a)); // number-NaN
// 说明: 非数
a = 'a';
console.log(typeof(Number(a)) + '-' + Number(a)); // number-NaN
// 说明: 非数
a = '1a';
console.log(typeof(Number(a)) + '-' + Number(a)); // number-NaN
// 说明: 非数
Number("0"); // 0
Number(""); // 0
Number(" "); // 0
Number(null); // 0
Number(false); // 0
Number([]); // 0
Number("\\n"); // 0
Number("\\t"); // 0
Number(true); // 1
Number('true'); // NaN
Number(undefined); // NaN
Number({}); // NaN
Number("x"); // NaN
ToString
抽象操作)。字符串开头的空白符将会被忽略。2
到 36
,表示字符串的基数。例如指定 16 表示被解析值是十六进制数。parseInt("123", 16) === parseInt("0x123") // output: 291
true
、false
、''
、null
、undefined
、NaN
、[]
、{}
都转为 NaN
var a = '123';
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-123
a = true;
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-NaN
// 说明: 只管把数字转为整型,非数都是 NaN
a = null;
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-NaN
// 说明: 同上
a = undefined;
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-NaN
a = NaN;
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-NaN
a = '3.14'
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-3
// 说明: 直接把小数点扔掉
a = 'abc123';
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-NaN
// 说明: 非数
a = '123abc';
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-123
// 说明: 如果数字和字符串混合,字符串在前,转为非数;数字在前,转到下一个不是数字为止的数字
a = '123.22abc';
console.log(typeof(parseInt(a)) + '-' + parseInt(a)); // number-123
a = '23.927'
// parseInt(a) → 23
var a = '10';
console.log(parseInt(a, 16)); // 16
// 说明: 第二个参数是基底,把xx进制的a转为10进制 xx范围 2~36
// 16 进制的 10 转 十进制 => 1*16^1 + 0*16^0 = 16 + 0 = 16
var a = 'b';
console.log(parseInt(a, 16));
// 说明: 0123456789abcdef
// 第 0 位:b * 16 ^ 0 = 11 * 1 = 11
parseInt('123', 5); // 把 '123' 看做5进制的数,返回十进制的数38
// => 1 * 5 ^ 2 + 2 * 5 ^ 1 + 3 * 5 ^ 0 = 25+10+3 = 38
parseInt(123)
123
parseInt("123")
123
parseInt("12a") // a不能解析成整数了
12
parseInt("a12a") // 第一个位置就不能解析了
NaN
parseInt(12, 0) // 12 10进制为12
12
parseInt(12, 5) // 12 5进制为7
7
parseInt(12, 8) // 12 8进制为10
10
parseInt(112, 2) // 112 2进制,前两位是可以解析的,第三位不能解析,2进制只有0 1
3
parseInt(112, 1) // 1进制只有0,第一位就不能解析
NaN
parseInt(123, 3) // 123 中 12 可以解析,3进制为5
5
var a = '3.1415926';
console.log(typeof(parseFloat(a)) + '-' + parseFloat(a)); // number-3.1415926
a = '3';
console.log(typeof(parseFloat(a)) + '-' + parseFloat(a)); // number-3
a = '3.14aab';
console.log(typeof(parseFloat(a)) + '-' + parseFloat(a)); // number-3.14
var num = parseFloat('3.1415926'); // 3.1415926
console.log(num.toFixed(2)) // 3.14
console.log(3.14621.toFixed(2)) // 3.15
// 说明: 四舍五入
false
、''
、0
、undefined
、null
、NaN
其他都为 true
Boolean(null) // false
// undefined null false 0 NaN "" 都转为 false
// 其余都是 true
window.isNaN
要先用 Number(xx)
转一下Number.isNaN
则不会隐式转换console.log(isNaN(NaN)); // true
console.log(isNaN(123)); // false
console.log(isNaN('a')); // true
// 说明: 'a'是非数,转不了number,就是NaN
console.log(isNaN('NaN'));
// true 全局isNaN 字符串非数就是NaN
console.log(Number.isNaN("NaN"));
// false Number下的isNaN,不会隐式转化
console.log(isNaN(null)); // false
// 说明: Number(null) -> 0, 0是数字,不是NaN
console.log(isNaN(undefined)); // true
// 说明: Number(undefined) -> NaN
转换规则:
valueOf()
,并确保返回值是基本类型valueOf
这个方法或者 valueOf
返回的类型不是基本类型,那么对象会继续调用 toString()
方法valueOf
和 toString()
方法,或者返回的都不是基本类型,那么直接抛出 TypeError
异常注意: 如果 preferedType=string,那么 2,3 顺序调换
转换实现:
对象 | valueOf() | toString() | 默认 preferedType |
---|---|---|---|
Object | 原值 | "[object Object]" | Number |
Function | 原值 | "function xyz() {...}" | Number |
Array | 原值 | "x,y,z" | Number |
Date | 数字(存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数)e.g. 1628864272562 | "Sat May 22 2021..." | String |
toString()
可以等效为 join(",")
,遇到 null、undefined 都被忽略,遇到 symbol 直接报错,遇到无法 ToPrimitive 的对象也报错模板字符串
或者使用 String(...)
包装时,preferedType=string,即优先调用 .toString()
减法
或者 Number(...)
包装时,preferedType=number,即优先调用 .valueOf()
[1, null, undefined, 2].toString(); // "1,,,2"
[1, Symbol('x')].toString(); // TypeError: Cannot convert a Symbol value to a string
[1, Object.create(null)].toString(); // VM3251:1 Uncaught TypeError: Cannot convert object to primitive value
console.log([].valueOf()); // []
console.log([].toString()); // ''
console.log({}.valueOf()); // {}
console.log({}.toString()); // [object Object]
console.log((function test (){ var a }).toString()); // function test (){ var a }
加减法运算中遵循了一些隐式转换规则:
1 + {}; // 1 + {}.toString() => 1 + "[object Object]" => "1[object Object]"
1 + [2, 3];
// 1 + [2, 3].toString(); => 1 + [2, 3].join(",") => 1 + "2,3" => "12,3"
[1] + [2, 3];
// ["1"].join(",") + [2, 3].join(",") => "1" + "2,3" => "12,3"
function test() {};
10 + test;
// 10 + (test).toString() => 10 + "function test() {}" => "10function test() {}"
10 - undefined;
// 10 - Number(undefined) => 10 - NaN => NaN
这里的任意值都是指基本类型,因为对象会先执行 ToPrimitive 变成基础类型
1 + "1"; // (1).toString() + "1" => "1" + "1" => "11"
1 + 1; // 2
"1" + 1; // "1" + (1).toString() => "1" + "1" => "11"
"1" + "1"; // "11"
1 + "1" + 1; // (1).toString() + "1" + (1).toString() => "1" + "1" + "1" => "111"
"1" + false; // "1" + "false" => "1false"
"1" + null; // "1" + "null" => "1null"
"X" + undefined + false; // "X" + "undefined" + "false" => "Xundefinedfalse"
"X" + {}; // "X" + ({}).toString() => "X" + "[object Object]" => "X[object Object]"
⭐️ [] + ''; // ([]).toString() + '' => '' + '' => ''
[null] + ''; // ([null]).toString() + '' => '' + '' => ''
[undefined] + ''; // ([undefined]).toString() + '' => '' + '' => ''
[1] + ''; // ([1]).toString() + '' => '1' + '' => '1'
这里的非字符串都是指基本类型,因为对象会先执行 ToPrimitive 变成基础类型
1 + true; // 1 + Number(true) => 1 + 1 => 2
1 + false; // 1 + Number(false) => 1 + 0 => 1
1 + null; // 1 + Number(null) => 1 + 0 = 1
1 + null + false + 1; // 1 + Number(null) + Number(false) => 1 + 0 + 0 + 1 => 2
1 + undefined; // 1 + Number(undefined) => NaN
1 + undefined + false; // 1 + Number(undefined) + Number(false) => 1 + NaN + 0 => NaN
🍒 1 + undefined + [1]; // 1 + Number(undefined) + ([1]).toString() => 1 + NaN + "1" => "NaN1"
🍒 1 + undefined + "1"; // 1 + Number(undefined) + "1" => 1 + NaN + "1" => "NaN1"
null + null; // Number(null) + Number(null) => 0 + 0 => 0
🍒 1 + ![]; // 1 + Number(!Boolean([])) => 1 + Number(!true) => 1 + 0 => 1 为什么是:1 + Number(!Boolean([])),先把 ![] 转化成 !Boolean([]) ,再变成 !true, 再变成 false, 就变成了 1 + false, 对于基本类型,加法中用 Number 转非数字类型,变成了 1+Number(false),最后成了 1 + 1
1 + !{}; // 1 + Number(!Boolean({})) => 1 + Number(!true) => 1 + 0 => 1
!{} + !{};
// Number(!Boolean({})) =>
// Number(!Boolean({})) =>
// Number(!true) + Number(!true) =>
// 0 + 0 => 0
此时的 preferedType === number
3 - 1; // 2
3 - '1'; // 3 - Number('1') => 3 - 1 => 2
'3' - 1; // Number('3') - 1 => 3 - 1 => 2
'3' - '1' - '2'; // Number('3') - Number('1') - Number('2') => 0
3 - []; // 3 - ([]).toString() => 3 - "" => 3 - Number("") => 3 - 0 => 3
3 - {};
// 3 - ({}).toString() =>
// 3 - "[object Object]" =>
// 3 - Number("[object Object]") =>
// 3 - NaN => NaN
var date = new Date();
date.toString(); // "Sun Aug 15 2021 16:05:17 GMT+0800 (China Standard Time)"
date.valueOf(); // 1629014717125
date + 1; // "Sun Aug 15 2021 16:05:17 GMT+0800 (China Standard Time)1"
date - 1; // 1629014717124
+ x
和 一元运算 +x
是等效的(以及 x
),都会强制 ToNumber+ 0; // 0
- 0; // 0
+ new Date();
// Number(Sun Aug 15 2021 16:09:47 GMT+0800 (China Standard Time)) =>
// 1629014965054
1 + + "1"; // 1 + Number("1") => 1 + 1 => 2
"1" + + "1"; // "1" + Number("1") => "1" + 1 => "11"
1 + + + + ["1"]; // 1 + (+(+(+["1"]))) => 1 + Number(["1"]) => 1 + 1 => 2
1 + - + - [1]; // 1 + (-(+(-[1]))) => 1 + (-(+(-Number([1])))) => 1 + 1 => 2
1 - + - + 1; // 1 - (+(-(+1))) => 1 - (-1) => 2
1 - + - + - 1; // 1 - (+(-(+(-1)))) => 1 - (1) => 0
💄1 + + [""]; // 1 + Number([""]) => 1
1 + + ["1", "2"];
// 1 + Number((["1", "2"].toString())) =>
// 1 + Number("1,2") =>
// 1 + NaN =>
// NaN
var a = '1';
a++; // 当执行 a++ 时,JavaScript 首先会将字符串 '1' 转换为数字 1。
console.log(a); // 2
var a = '1';
a += 1; // a = a + 1 => a = '1' + 1 => a = '11'
console.log(a); // '11'
[] + {};
// ([]).toString() + ({}).toString() => "" + "[object Object]" => "[object Object]"
{} + []; // 0
{ a: 2 } + []; // 0
第一个,这时 {} 其实代表的是代码块,最后就变成了 +[],根据前面的原则,数组先被转换成字符串"",接着因为+x的运算,字符串被转成数字0。
第二个,此时a不是代表对象属性,而是被当成了标签(label),标签这东西IE6就已经有了。所以如果我们写成对象是会报错的,逗号要改成分号才能通过编译。
// Uncaught SyntaxError: Unexpected token ':'
{ a: 2, b: 3 } + []
// 分号OK
{ a: 2; b: 3 } + [] === 0;
⚠️注意:在 Node >= 13.10.0 的版本,{}
被优先解释为空对象,仅在非对象结构的情况才会被认为是代码块。
// nodeJs >= 13.10.0 的运行结果
{} + []; // "[object Object]";
{ a: 2 } + []; // "[object Object]";
{ a: 2, b: 3 } + []; // "[object Object]";
// 注意是分号,当成代码块
{ a: 2; b: 3 } + []; // 0
// 有JS语句或者表达式,当成代码块
{ var a = 1; } + []; // 0
{ ; } + []; // 0
{ 123 } + []; // 0
{ 1 + 2 } + []; // 0
定论还是下的太早了,我们还是有办法让引擎优先处理成代码块:
// 所有node版本
;{} + [] === 0;
;{ a: 2 } + [] === 0;
// Uncaught SyntaxError: Unexpected token ':'
;{ a: 2, b: 3 } + [];
如果在表达式中有symbol类型,那么就会直接报错。比如1 + Symbol("x")
报错如下:
Uncaught TypeError: Cannot convert a Symbol value to a number
加法和减法的唯一不同就在:在最终把所有非基本类型通过 ToPrimitive
转成基本类型后:
相对于全等都需要对类型进行判断,当类型不一致时,宽松相等会触发隐式转换。下面介绍规则:
console.log({} != {}); // true
console.log([] != {}); // true
console.log([] != []); // true