iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 8
0

強制轉型,這是進入 JavaScript 陣營後,需要防守的重要一環。
尤其是隱性強制轉型。在不了解的情況下,相當捉模不定,重點還是玩遊戲的規則呀。

  • ToString 時,會調用 Object.prototype.toString()。
  • ToNumber 時,會調用 valueOf()的方法,如果沒有會再找 toString()。都沒有就是 typeError。

轉換值

兩種強制轉型英文的差異。

轉型 強弱型別 時間點
casting ( conversion ) statically compile time
coercion dynamically runtime
  • 強制轉型 ( coercion )

JavaScript 強制轉型的結果,永遠都會是純量的基本型別值之一。不會有像 object, function 這樣複雜的值。

var a = 42;
var b = a + "";    // 隱含的強制轉型 ( implicit coercion )
var c = String(a); // 明確的強制轉型 ( explicit coercion )

了解兩種轉型的區別,和程式碼是要給其他協作成員看的。

抽象的值運算

To String

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 是一種網路世界資訊交換的格式。

整篇內容在講 JSON.stringify(..)

(這邊是稍微岔開話題,這個稍微有六頁的篇幅,請讀者自行閱讀。是用 JSON.stringify(..) 的方法擬似的介紹 .toString()。)

還是附上結論

JSON.stringify(..) 並不是某種形式的強制轉型。放在這是因為兩個關聯到 toSting 強制轉型的原因。

  1. string, number, boolean 以及 null 這些值字串化為 JSON 的方式基本上就跟他們經由toString 抽象運算的規則強制轉型的 string 值的方式相同。
  2. 如果你傳入一個 object 值給 JSON.stringify(..),而那個 object 尚有一個 toJSON() 方法,這個 toJSON() 會在字串化之前自動被呼叫,來將該值(可以算是)「強制轉型」為 JSON - safe 的。

To Number

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

toBoolean()

這個主題有很多混淆和誤解的存在,所以請專心細讀。

Falsy 值

  • undefined
  • null
  • false
  • +0、-0,以及 NaN
  • ""

沒有在這個清單上的都是 truthy,請牢記 falsy 清單。
Truthy 的意思是,經過 boolean 轉換會變成 true。

Falsy 物件

先用各種物件包覆各種 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 即可。(這尛?)

document.all 就是一個特別的存在,falsy object。

Truthy 值

除了 falsy 就是 Truthy。

參考資料

  1. 你所不知道的 JS
  2. MDN - JSON.stringify()
  3. Wiki - JSON
  4. ECMAScript® Language Specification

上一篇
Day7 - Native 適合用來做建構器嗎?
下一篇
Day9 - 明確的強制轉型
系列文
你為什麼不問問神奇 JavaScript 呢?30

尚未有邦友留言

立即登入留言