javascript: 类型转换
strat
javascript 的类型转换一直是个大坑,但其实它也减少了代码量。
toprimitive
symbol.toprimitive 是一个内置的 symbol 值,它作为对象的函数值属性存在,当一个对象转换为原始值时,会调用此函数。
该函数被调用时,会被传递一个字符串参数 hint
,表示要转换到的原始值的预期类型。 hint
参数的取值是 "number"
、"string"
和 "default"
中的任意一个。
// 一个没有提供 symbol.toprimitive 属性的对象,参与运算时的输出结果 let obj1 = {}; console.log(+obj1); // nan console.log(`${obj1}`); // "[object object]" console.log(obj1 + ""); // "[object object]" // 接下面声明一个对象,手动赋予了 symbol.toprimitive 属性,再来查看输出结果 let obj2 = { [symbol.toprimitive](hint) { if (hint == "number") { return 10; } if (hint == "string") { return "hello"; } return true; } }; console.log(+obj2); // 10 -- hint 参数值是 "number" console.log(`${obj2}`); // "hello" -- hint 参数值是 "string" console.log(obj2 + ""); // "true" -- hint 参数值是 "default"
从上面可以看出,toprimitive 转换过程依靠 hint 参数:
-
number
: valueof() → tostring() → typeerror -
string
: tostring() → valueof() → typeerror -
default
: 同 number
valueof
对象 | 返回值 |
---|---|
array | 返回数组对象本身。 |
boolean | 布尔值。 |
date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 utc。 |
function | 函数本身。 |
number | 数字值。 |
object | 对象本身。这是默认情况。 |
string | 字符串值。 |
symbol | symbol本身 |
math 和 error 对象没有 valueof 方法。 |
tostring
对象 | 返回值 |
---|---|
array | [1, 2, 3] => "1,2,3" |
boolean | false => "false" |
date | 返回表示 utc 的字符串。 |
function | 返回表示当前函数源代码的字符串。 |
number | 返回表示当前数值的字符串。 |
object | "[object object]" |
string | 字符串本身。 |
symbol | "symbol()" |
注意:[null].tostring()
以及[undefined].tostring()
均返回空字符串""
。
toboolean
es5 规范 9.2中列举了布尔强制类型转换 (toboolean) 会出现假值 (false) 的仅有以下几个,其余都为真值 (true):
- undefined
- null
- false
- +0、-0、nan
- ''(空字符串)
/* 以下 a、b、c 存储的是指向对象的指针,并非假值 */ let a = new number(0); let b = new boolean(false); let c = new string(''); boolean(a) // true boolean(b) // true boolean(c) // true boolean(0) // false boolean(false) // false boolean('') // false
tonumber
对象 | 返回值 |
---|---|
undefined | nan |
null | 0 |
boolean | true => 1, false => 0 |
number | 返回自身 |
string | 不能解析为 stringnumericliteral 的,均返回 nan |
object | toprimitive(input argument, hint number) |
强制类型转换符
加号 (+)
+
作为一元运算符,单独使用,会强制将右侧操作数类型转为 number,即对右侧操作数使用 tonumber()。
+1 // 1 +'1.2' // 1.2 +[] // 0 +[1, 2, 3] // nan +{} // nan
叹号 (!)
!
会强制将右侧操作数类型转为 boolean,并反转真、假值,即对右侧操作数使用 !toboolean()。
!true // false !0 // true ![] // false !'' // true !undefined // true !null // true !!true // true !!undefined // false !!null // false
四则运算符
加法运算遵循以下规则:
-
运算的其中一方为字符串,就会把另一方转换为字符串。
1 + '1' // '11' 42 + '' // '42'
-
如果其中一方不是字符串或数字,就会将它转换为字符串或数字。
false + true // 1 3 + [1, 2, 3] // '31,2,3' ([] + {}) // '[object object]' /* {} + [] 的结果为 0, 是因为从左往右解析,{} 为一个代码块,+[] 被解析为将 [] 转为 number, 即 0。*/ {} + [] // 0 ({} + []) // "[object object]"
注意:
/* 会出现以下情况,是因为 + 'b' 解释为 tonumber('b') */ 'a' + + 'b' // "anan"
对于加法运算以外的运算来说,双方会被转为数字进行运算。
1 * '2' // 2 [] * {} // nan 1 * [1, 2, 3] // nan let obj = { valueof: () => { return 1 } } obj * 2 // 2
== and ===
对于==
(相对等于)、===
(绝对等于),绝大部分的书籍和博客都解释为前者仅检查值是否相等,后者检查值和类型是否相等,其实这样是不对的,正确的解释应该是:前者允许在比较的时候进行强制类型转换,后者不允许。
es5 规范 11.9.3 定义了相对等于的行为,涵盖了所有的类型,具体可分为以下几种情况:
-
双方类型相同
类型 结果 undefined true null true number 1. 如果其中一方为nan,返回false。2. 如果 x 与 y 的值相同,则返回true,否则false。3.如果其中一方为+0或-0且另一方为+0或-0,返回true。 string 双方为完全相同的字符序列,返回true。否则返回 false。 boolean 双方为true或false,返回true,否则返回false。 object 双方引用同一个对象,返回 true。否则,返回false nan == nan // false -0 == +0 // true
-
null 与 undefined
null == undefined // true
-
字符串与数字
会将字符串转为数字进行比较,即
tonumber(字符串) == 数字
。10 == '10' // true 10 == 'a' // false /* 十六进制 '0xa' => 十进制 10 */ 10 == '0xa' // true
-
布尔类型与其他类型
会将布尔类型转为数字,再与其他类型进行比较,即
tonumber(布尔类型) == 其他类型
0 == false // true '1' == true // true null == false // false undefined == false // false
-
对象类型与非对象类型
会将对象类型转为原始类型,再进行比较,即
toprimitive(对象类型) == 非对象类型
[1] == 1 // true [1, 2] == 1 // false /* b.tostring() 返回 '111' */ let a = '111'; let b = object(a); a == b // true /* null 与 undefined 不能被封装为 object, 即 object(null) 的返回结果与 object() 的一样 */ let c = null; let d = object(c); c == d // false let e = undefined; let f = object(e); e == f // false
难以理解的情况
-
[] == ![]
[] == ![] // true /* 第一步: !的优先级比 == 高,所以 ![] 解析为 !boolean([]),结果为 true. 现在: [] == true 第二布: 布尔类型与其他类型进行比较,解析为 tonumber(true), 结果为 0. 现在: [] == 0 第三步: 对象类型与非对象类型进行比较,解析为 toprimitive([], 'number'),结果为 0. 现在: 0 == 0 // true */
-
[null] == ''
[null] == '' // true [undefined] == '' // true /* [null].tostring() 以及 [undefined].tostring() 均返回空字符串 '' 因为 null 与 undefined 均没有 tostring 和 valueof 方法。 */
-
0 == '\n'
0 == '\n' // true 0 == '\t\r\n' // true /* 上述语句被解析为 tonumber('\n'), 返回结果为 0. */
具体解释:
备注
理解了类型转换,你会发现并非一定要抛弃==
去使用===
。
上一篇: django-分页