強制轉型,這是進入 JavaScript 陣營後,需要防守的重要一環。
尤其是隱性強制轉型。在不了解的情況下,相當捉模不定,重點還是玩遊戲的規則呀。
兩種強制轉型英文的差異。
轉型 | 強弱型別 | 時間點 |
---|---|---|
casting ( conversion ) | statically | compile time |
coercion | dynamically | runtime |
JavaScript 強制轉型的結果,永遠都會是純量的基本型別值之一。不會有像 object, function 這樣複雜的值。
var a = 42;
var b = a + ""; // 隱含的強制轉型 ( implicit coercion )
var c = String(a); // 明確的強制轉型 ( explicit coercion )
ES5 規格的 section 9.8 為此定義了 toString 的抽象運算。
// 1.07 乘以 1000 七次
var a = 1.07*1000*1000*1000*1000*1000*1000*1000;
var b = true;
var c = [1,2,3];
var d;
var e = null;
var f = {aa: 1};
a.toString(); // "1.07e21"
b.toString(); // "true"
c.toString(); // "1,2,3"
d.toString(); // Uncauht TypeError
e.toString(); // Uncauht TypeError
f.toString(); // "[object object]"
f.toString(); 預設是 Object.prototype.toString() 是回傳內部的 [[Class]]。所以,加上指向 .call(); 可以回傳特定的內部 [[Class]],參考第六章 Native。
f.toString.call(d); // "[object Undefined]"
Array 有一個複寫 toString() 的預設值,他進行字串化的方式如下
var a = [1,2,3];
a.toString(); // "1,2,3"
toString() 可以明確地被呼叫,或是當一個非 string 被用在 string 的情境中,會自動呼叫。
JSON 是一種網路世界資訊交換的格式。
整篇內容在講 JSON.stringify(..)
(這邊是稍微岔開話題,這個稍微有六頁的篇幅,請讀者自行閱讀。是用 JSON.stringify(..) 的方法擬似的介紹 .toString()。)
還是附上結論
JSON.stringify(..) 並不是某種形式的強制轉型。放在這是因為兩個關聯到 toSting 強制轉型的原因。
- string, number, boolean 以及 null 這些值字串化為 JSON 的方式基本上就跟他們經由toString 抽象運算的規則強制轉型的 string 值的方式相同。
- 如果你傳入一個 object 值給 JSON.stringify(..),而那個 object 尚有一個 toJSON() 方法,這個 toJSON() 會在字串化之前自動被呼叫,來將該值(可以算是)「強制轉型」為 JSON - safe 的。
ES5 規格的 section 9.3 為此定義了 toNumber 的抽象運算。
Number(true); // 1
Number(false); // 0
Number(undefined); // NaN
Number(null); // 0
(因為 .toNumber() 無法看出 undefined 和 null 的狀態,所以用先前的 native 做轉型介紹,並不推薦實戰這麼做。)
ToNumber 時,會調用 valueOf()的方法,如果沒有會再找 toString()。都沒有就是 typeError。
var a = {
valueOf: function(){
return "42"; // 覆寫 valueOf()
}
};
var b = {
toString: function(){
return "42"; // 覆寫 toString()
}
};
var c = [4,2];
c.toString = fuction(){
return this.join( "" ); // "42" 陣列轉字串。
};
Number(a); // 42
Number(b); // 42
Number(c); // 42
Number( "" ); // 0
Number( [] ); // 0
Number( ["abc"] ); // NaN
這個主題有很多混淆和誤解的存在,所以請專心細讀。
沒有在這個清單上的都是 truthy,請牢記 falsy 清單。
Truthy 的意思是,經過 boolean 轉換會變成 true。
先用各種物件包覆各種 falsy,再用 && 邏輯判斷下進行轉型。
var a = new Boolean( false );
var b = new Number( 0 );
var c = new String( "" );
var d = Boolean( a && b && c );
d; // true
只有 a b c 都為 true,d 才會 true。
既然物件都會是 true,那有 Falsy 物件嗎?
他們可能出現在你的程式中,但他們實際上並非 JavaScript 的一部分。
就是 document.all。歷史解說開始。
過去常用 document.all 強制轉型為 boolean,因為要判定是否為舊的、非標準 IE。如果 true 就執行內部屬於 IE 的部分。相當多!
if (document.all) {
/* IE 的運行程式 */
}
但後來 IE 持續修正後,為了提升瀏覽體驗,必須要內部程式碼/* IE 的運行程式 */
不再運行。
方案是,直接讓 document.all 在 JS 中是 falsy 即可。(這尛?)
除了 falsy 就是 Truthy。