strat javascript 的類型轉換一直是個大坑,但其實它也減少了代碼量。 ToPrimitive Symbol.toPrimitive 是一個內置的 Symbol 值,它作為對象的函數值屬性存在,當一個對象轉換為原始值時,會調用此函數。 該函數被調用時,會被傳遞一個字元串參數 ,表示要轉換 ...
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() → TypeErrorstring
: toString() → valueOf() → TypeErrordefault
: 同 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. */
備註
理解了類型轉換,你會發現並非一定要拋棄==
去使用===
。