iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 10
1
Modern Web

跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset系列 第 10

[day09] YDKJS (Coercion:spec) : Coercion 型別轉換 ? Boxing Wrappers ?

  • 無關痛癢純發洩的前言

估計 30 天只能寫到第二本 Orz

本來預計 30 天打發時間,整理一下筆記就可以出文章,其他時間研究 React。
結果一直查 spec 查到回過神,
一篇看起來文章沒多長,但資訊密度爆炸多的爛文,我寫得爛 :(
平均每一篇寫 4 小時以上,

我的 React 沒什麼時間看 ,也沒什麼時間看太多人的文章 QQ
還在思考繼續提高文章密度讀者會不會直接拉到最下面 skip ...

型別轉換 Coercion

有些人可能會說,「我寫 JavaScript 不會有 型別轉換(coercion) 問題,型別轉換(coercion) 是 JavaScript 最怪、最多 bug 的地方,我不用就可以了。 」

但是呢,其實不可能避免 型別轉換(coercion)。
以下舉幾個隱性的型別轉換,但幾乎天天用的例子。

ES6 最常用的幾個功能之一 : 樣板字面值(Template literals)

這個是大家最熟悉的好朋友,我相信很少人願意自己慢慢用 + 去串接字串。

猜猜看會發生什麼?
哇,你好聰明哦。是 型別轉換(coercion) 呢! (被打

484 開始覺得,其實有 型別轉換(coercion) 自動處理型別問題,真的寫起來潮爽德~
順帶一提,這邊圈起來的地方,前面提過非常多次但都沒有講到的加法 多載XD


plus operator (+) : operator overloading 運算子多載

快速記憶方法 : plus operator (+) 偏好 hint string --> ToString() 抽象運算子

  • ToString() a.k.a. ToPrimitive() , 如果不確定意思,參考前兩天文章。

[day07] YDKJS (Type/Coercion) : Type總結 和 abstract operations (Spec.)
[day08] YDKJS (Coercion:spec) : 回來讀 spec.  ToString(), ToNumber() , ToBoolean()

  • plus operator (+) 兩邊任一邊有 string , 都會做 ToString()

補個舊版中文,一樣是第 7 點

當然,你可以很固執故意這樣寫,然後說沒有用到 型別轉換(coercion)

  • array join 的 build-in function, 預設回傳值的型別 是 string.

  • another :

但上面的例子也觸發 型別轉換(coercion), 因為 Primitive type 沒有 method,
後面會提到,這也是一種隱性的型別轉換(implicit coercion) 。

先大概補個坑(後面會提到 用太多次了) :
如果你前面文章的 reference 有認真看,應該會知道 toString() 本身會被覆寫,所以要看本身怎麼被觸發的,也就是前面跟的對象是誰。
這邊的 numStudents 其實先 boxing 成 string objects 再取用 String.toString() ,boxing 的瞬間就完成 coercion 動作。

boxing 的原因是因為數字是原始型別,沒有方法可以調用,可以調用的原因是背後 Boxing Wrappers 機制。詳細 Boxing Wrappers 後面文章會提到,書裡面也有寫。

Best way: 最明確(explicit)且大家都可以辨識的型別轉換(coercion)

如果你不想出現任何 隱性型別轉換(implicit coercion),讓你的 code 看起來很奇怪或是像魔法(magical things),那就用明確型別轉型(explicit coercion) 。


other sample : 很像魔法的轉換

如果使用到大量表單,在做表單處理時,很容易變成字串相加 :

常見會這樣處理:

  • 一個魔法的 Unary Operator (+)

一樣給個舊版中文參考

但其實是 Unary Operator (+) , 一元運算子 (+) 觸發 型別轉換(coercion),
實際是 ToPrimitive() hint number => ToNumber()

  • return +inputElement.value
    ==> ""(空字串) + inputElement.value
    ==> 0 + inputElement.value
    ==> Number + Number = Number

註:不要忘記 toNumber("") // 空字串 ==> 0

如果你不想出現 magical things,考慮以下用法:
Kyle Simpson 也偏好這樣使用,因為一元運算子很容易和加法運算子混肴。

補充:

  • 減法 operator 沒有 運算子多載 :

減法只做 數字運算(numeric) hint toNumber()
如果丟字串就回傳 NaN 。


小複習整理 : 比較下列兩者

var numVar = 20 ;

 var q_1 =  +numVar;  // 0  (數字)  => 一元運算子 hint number
 var q_2 =  "" + numVar ; // '0' (字串) => 加法運算子 ,如果有string , hint 就是 string ,做字串相加

書本內的瘋狂例子 :
1 + - + + + - + 1; // 2

主要的知識點是 一元運算的正負會抵消,但是連續 + +(有空格) 和 ++ (遞增運算子)不同

1 + - + + + - (+ 1); // 一元運算正號 會轉數字
1 + - + + + - (1); // 一元運算負號 也會轉數字,如果可以數字結束後再加上負號
1 + - + + + (- 1); // 遇到連續 一元運算正號,一樣轉數字(連續消除三個 一元運算正號 ),結果還是 -1
1 + - (- 1); // 一元運算負號 再加上負號 , -(-1) = 1

1 + 1;
這邊是二元加法符號,兩邊都是數字就純數字相加 = 2 。
如果開頭是字串就變成字串相加 "1" + 1 = "11"

Reference


Boolean 不要做任何相等 改用條件

  • 不要把 Boolean(0) 當作 false , 如 if(studentsInputElem.value){...}
  • 要使用明確的條件定義 dothing.length > 0 ,才有意義。

假設 studentsInputElem.value = 0if(0) === false 語意上來說不合理 。
真實世界的數值 0 其意義不會 falsy ,條件才有可能是 falsy。

直接使用 Boolean(things) 是最優雅的 Coercion 方法

  • 兩個 ! negate operator 只是為了 Coercion 到 Boolean。
  • 建議使用 Boolean(var) ,閱讀比較直觀。

註:Boolean 只是純查表,不會做演算法遞迴。

  • 上述範例一直使用 ! ,來看看 spec. 和一些使用情境。

舊版中文

function Account(cash) {
    this.cash = cash;
    this.isCashHere = !cash;
    this.hasMoney = !!cash;
    this.useThis = Boolean(cash);
}

var account = new Account(100);
console.log(account.cash);      //100
console.log(account.isCashHere); // !(true) === false  ?! 小心不要用錯
console.log(account.hasMoney);  // !!(true) === !(false) === true
console.log(account.useThis);  // true ,純查表

還是再三提醒

Boolean 真的只是查表,所以第 8 行 不會變成空字串。


Boxing Wrappers

原始型別 Primitive type 怎麼做到 Objects 可以做到的事?

因為 JavaScript 認為自己很聰明,幫你在 JavaScript認為合適的地方 都隱性轉型(implicit coercion)成物件。

如果你寫 Java , 可以這樣理解 new String("abc")
但是你寫的是 JavaScript , 用 new 會產生很多狀況,比如產生物件實體:

var a = new Boolean( false ); // a 這時候是 truthy,因為是物件

if (!a) {   // !truthy = false ,永遠不會執行
	console.log( "Oops" ); // never runs
}

其實我前面寫過兩次了。
Reference code : MDN

typeof new Boolean(true) === 'object'; // 這樣會令人混淆。不要這樣用!
typeof new Number(1) === 'object'; // 這樣會令人混淆。不要這樣用!
typeof new String("abc") === 'object';  // 這樣會令人混淆。不要這樣用!

順帶一提,其實這個術語沒有出現在 spec.中,
然後書上也沒有寫,本來想追看看 spec. 有沒有規範這個狀態 如何實作,
不過 spec. 只有寫入 [[internalSlots]] 。 也可能是我理解有錯。

abstract operation : ToObject 7.1.13

  1. 使用 . 的時候呼叫 abstract operation 的 ToObject , 並傳入 argument

可以解釋 Undefined , Null Throw a TypeError exception.

  1. internal slot( [[internalSlots]] ) 寫入 如 [[BooleanData]] 的標記 (tag)
    // 這邊用 Boolean 當範例
  2. 其實這邊之後就是偏向 Constructor 的細節,因為依照演算法物件有被建立。
  3. 演算法內有 internal slot 就可以產生物件,該物件內的 Constructor 演算法會辨認是不是來自 [[BooleanData]] 的標記 (tag)

舉例 初始化 Constructor 的演算法 來說,
OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] )
依序傳入三個參數,由此辨認是來自 constructor 還是 Proto,最後才是 ToObject 寫入的 [[internalSlots]] 。

後面就沒追了,因為是 Constructor 的細節。


上一篇
[day08] YDKJS (Coercion:spec) : 來讀 Spec.  ToString(), ToNumber() , ToBoolean()
下一篇
[day10] YDKJS (Coercion) : 如何決定何時使用「顯性」或是「隱性」的型別轉換?
系列文
跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言