|
45 | 45 | `"default"` |
46 | 46 | : 發生於極少情況下,當 "不確定" 運算子會預期是什麼類型時。 |
47 | 47 |
|
48 | | - 舉個例,二元加法 `+` 可以同時運作在字串(串接)和數值(相加)上,所以字串與數值兩者皆可使用。或當物件使用 `==` 比較字串、數值或符號時,哪種轉換會進行也很不清楚。 |
| 48 | + 舉個例,二元加法 `+` 可以同時運作在字串(串接)和數值(相加)上,所以字串與數值兩者皆可使用。因此若二元加法以物件作為引數時,它會使用 `"default"` 提示來轉換。 |
| 49 | + |
| 50 | + 同樣地,若物件使用 `==` 比較字串、數值或符號時,哪種轉換會進行也很不清楚,所以會使用 `"default"` 提示。 |
49 | 51 |
|
50 | 52 | ```js |
51 | | - // 二元加法 |
52 | | - let total = car1 + car2; |
| 53 | + // 使用 "default" 提示的二元加法 |
| 54 | + let total = obj1 + obj2; |
53 | 55 |
|
54 | | - // obj == string/number/symbol |
| 56 | + // obj == number 使用 "default" 提示 |
55 | 57 | if (user == 1) { ... }; |
56 | 58 | ``` |
57 | 59 |
|
58 | | - 大於/小於 運算子 `<>` 一樣可以同時作用於字串和數值上。但它使用 "number" 提示而非 "default",這是因為歷史因素。 |
| 60 | + 大於/小於 運算子 `<>` 一樣可以同時作用於字串和數值上。但它仍使用 "number" 提示而非 "default",這是因為歷史因素。 |
| 61 | + |
| 62 | + 實際上,我們不需要記住這些罕見的細節,因為除了某種例外(`Date` 物件,我們晚點會學到),所有內建物件都實作了和 `"number"` 一樣的 `"default"` 轉換,所以可以同樣方式運作。 |
59 | 63 |
|
60 | | - 實際上,除了某種物件(`Date` 物件,我們晚點會學到),所有內建物件都實作了和 `"number"` 一樣的 `"default"` 轉換,且我們或許也該這樣做。 |
| 64 | +```smart header="No `\"boolean\"` hint" |
| 65 | +請注意 -- 只有三種提示,就那麼簡單。 |
61 | 66 |
|
62 | | -請注意 -- 只有三種提示,就那麼簡單,不存在 "布林" 提示(所有物件在布林上下文中都是 `true`)等其它的。且若我們對 `"default"` 和 `"number"` 一視同仁,如同大多內建物件那樣,那將只有兩種轉換了。 |
| 67 | +不存在 "布林" 提示(所有物件在布林上下文中都是 `true`)等等其它的轉換。且若我們將 `"default"` 和 `"number"` 一視同仁,如同大多內建物件那樣,那將只有兩種轉換了。 |
| 68 | +``` |
63 | 69 |
|
64 | 70 | **要做轉換時,JavaScript 試著找尋並呼叫三種物件方法:** |
65 | 71 |
|
@@ -110,7 +116,29 @@ alert(user + 500); // hint: default -> 1500 |
110 | 116 | - 對於 "string" 提示,`toString -> valueOf`。 |
111 | 117 | - 否則,`valueOf -> toString`。 |
112 | 118 |
|
113 | | -舉個例,這個 `user` 採用 `toString` 與 `valueOf` 做了如同前述的事情: |
| 119 | +這些方法必須回傳一個原生值,若 `toString` 或 `valueOf` 回傳一個物件,則它將被忽略(視同根本不存在這個方法)。 |
| 120 | + |
| 121 | +每個物件會有下列預設的 `toString` 與 `valueOf` 方法: |
| 122 | + |
| 123 | +- `toString` 方法回傳一個字串 `"[object Object]"`。 |
| 124 | +- `valueOf` 方法回傳物件自身。 |
| 125 | + |
| 126 | +在此演示一下: |
| 127 | + |
| 128 | +```js run |
| 129 | +let user = {name: "John"}; |
| 130 | + |
| 131 | +alert(user); // [object Object] |
| 132 | +alert(user.valueOf() === user); // true |
| 133 | +``` |
| 134 | + |
| 135 | +所以若我們試圖將某個物件作為字串使用,像是用於 `alert` 等其它地方,那我們預設將看到 `[object Object]`。 |
| 136 | + |
| 137 | +為了避免搞混,而在這邊提到預設的 `valueOf` 只是為了完整性考量。如同你所見,它回傳了該物件本身從而被忽略了。別問我為什麼,那是歷史因素。所以我們可以假設它根本不存在。 |
| 138 | + |
| 139 | +來實作這些方法吧。 |
| 140 | + |
| 141 | +舉個例,這個 `user` 使用 `toString` 和 `valueOf` 的組合做了如同上述的事,而不是用 `Symbol.toPrimitive`: |
114 | 142 |
|
115 | 143 | ```js run |
116 | 144 | let user = { |
@@ -167,35 +195,41 @@ alert(user + 500); // toString -> John500 |
167 | 195 | 相對地,`Symbol.toPrimitive` *必須* 回傳一個原生類型,否則就會產生錯誤。 |
168 | 196 | ``` |
169 | 197 |
|
170 | | -## 進一步的操作 |
| 198 | +## 進一步的轉換 |
171 | 199 |
|
172 | | -一個發起轉換的運算獲得了原生類型,然後繼續運作,並在需要時再套用進一步的轉換。 |
| 200 | +如同我們所知的,許多運算子與函式都會做類型轉換,像是:乘法 `*` 會轉換運算元為數值。 |
| 201 | + |
| 202 | +若我們傳入一個物件作為引數,那將會進行兩個步驟: |
| 203 | +1. 物件被轉為原生值(使用上述的規則)。 |
| 204 | +2. 若轉完的原生值並非正確類型,就再進行轉換。 |
173 | 205 |
|
174 | 206 | 舉個例: |
175 | 207 |
|
176 | | -- 數學運算,除了二元加法以外,將會轉換該原生類型為數值: |
| 208 | +```js run |
| 209 | +let obj = { |
| 210 | + // 在少了其他方法的情況下,toString 處理所有的轉換 |
| 211 | + toString() { |
| 212 | + return "2"; |
| 213 | + } |
| 214 | +}; |
177 | 215 |
|
178 | | - ```js run |
179 | | - let obj = { |
180 | | - // 在少了其他方法的情況下,toString 處理所有的轉換 |
181 | | - toString() { |
182 | | - return "2"; |
183 | | - } |
184 | | - }; |
| 216 | +alert(obj * 2); // 4,物件被轉為原生類型 "2",然後乘法會讓它再變成一個數值 |
| 217 | +``` |
185 | 218 |
|
186 | | - alert(obj * 2); // 4,物件被轉為原生類型 "2",然後乘法會讓它再變成一個數值 |
187 | | - ``` |
| 219 | +1. `obj * 2` 的乘法運算會先轉換該物件為原生值(也就是變成一個字串 `"2"`)。 |
| 220 | +2. 然後 `"2" * 2` 會變成 `2 * 2`(字串被轉換為數值)。 |
188 | 221 |
|
189 | | -- 二元加法在同樣情境時會連接字串: |
190 | | - ```js run |
191 | | - let obj = { |
192 | | - toString() { |
193 | | - return "2"; |
194 | | - } |
195 | | - }; |
| 222 | +二元加法在同樣情境時會連接字串,並欣然接受轉完的字串: |
196 | 223 |
|
197 | | - alert(obj + 2); // 22(轉換成原生類型會回傳一個字串 => 串接) |
198 | | - ``` |
| 224 | +```js run |
| 225 | +let obj = { |
| 226 | + toString() { |
| 227 | + return "2"; |
| 228 | + } |
| 229 | +}; |
| 230 | + |
| 231 | +alert(obj + 2); // 22("2" + 2),轉換為原生值時回傳一個字串 => 串接 |
| 232 | +``` |
199 | 233 |
|
200 | 234 | ## 總結 |
201 | 235 |
|
|
0 commit comments