JavaScript隱式型別轉換(詳解 +,-,*,/,==)
阿新 • • 發佈:2020-03-22
JavaScript 在 運算 或 比較 之前, 會自動進行隱式型別轉換. 下面我們來仔細講一講 + - * / == 運算子經歷了哪些過程.
## 型別轉換
ECMAScript 執行時系統會在需要時從事自動型別轉換。為了闡明某些結構的語義,定義一集轉換運算子是很有用的。__這些運算子不是語言的一部分__;在這裡定義它們是為了協助語言語義的規範。
* ToPrimitive
* ToNumber
* ToString
* ToBoolean
* ToInteger
* ToInt32:(32 位有符號整數)
* ToUint32:(32 位無符號整數)
* ToUint16:(16 位無符號整數)
* ToObject
下面詳細講講前三種
### ToPrimitive
轉為原始型別.
```js
ToPrimitive(input, PreferredType)
```
|輸入型別|結果|
|-|-|
|Undefined|結果等於輸入的引數(不轉換)。|
|Null|結果等於輸入的引數(不轉換)。|
|Boolean|結果等於輸入的引數(不轉換)。|
|Number|結果等於輸入的引數(不轉換)。|
|String|結果等於輸入的引數(不轉換)。|
|Object|返回該物件的預設值。
物件的預設值由把 PreferredType 傳入作為hint引數呼叫物件的內部方法[[DefaultValue]]得到| #### 物件內部方法 [[DefaultValue]] (hint) 當用 String hint 呼叫 O 的 [[DefaultValue]] 內部方法,採用以下步驟: 1. str = O.toString(); 2. 如果str為原始值,返回str; 3. val = O.valueOf(); 4. 如果val為原始值,返回val; 5. 丟擲一個 TypeError 異常。 當用 Number hint 呼叫 O 的 [[DefaultValue]] 內部方法,採用以下步驟: 1. val = O.valueOf(); 2. 如果val為原始值,返回val; 3. str = O.toString(); 4. 如果str為原始值,返回str; 5. 丟擲一個 TypeError 異常。 __當不用 hint 呼叫 O 的 [[DefaultValue]] 內部方法時, 如果 O 為Date, 則hint=String, 除此之外, hit=Number。__ 例子: ```js ToPrimitive({}) 解析: 資料型別為 Object, 呼叫[[DefaultValue]] 沒有用 hint 呼叫, 則 hint 為 Number val = O.valueOf(); val 是 {}, 不是原始值 str = O.toString(); str 是 '[object Object]', 是原始值, 返回'[object Object]' 結果: '[object Object]' ``` ### ToNumber 轉為數字型別.(是 的實現)
|Input Type|Result|
|-|-|
|Undefined|NaN|
|Null|+0|
|Boolean|true: 1, false: +0|
|Number|結果等於輸入的引數(不轉換)。|
|String|將字串轉換為數字。|
|Object|Apply the following steps:
1. Call ToPrimitive(input argument, hint Number).
2. Call ToNumber(Result(1)).
3. Return Result(2).| ### ToString 轉為字串型別.(是 的實現)
|輸入型別|結果|
|-|-|
|Undefined|"undefined"|
|Null|"null"|
|Boolean|如果引數是 true,那麼結果為 "true"。
如果引數是 false,那麼結果為 "false"。| |Number|將數字轉換為字串。| |String|結果等於輸入的引數(不轉換)。| |Object|Apply the following steps:
1. Call ToPrimitive(input argument, hint String).
2. Call ToString(Result(1)).
3. Return Result(2).| 例子: ```js var obj = { valueOf: function () { return 1; }, toString: function () { return 2; } } Number(obj) 解析: obj 型別 Object, 呼叫 ToPrimitive(obj, Number) 用 Number hint 呼叫 obj 的 [[DefaultValue]] 內部方法 呼叫 obj 的 valueOf 方法, 結果為 1 返回 ToNumber(1) 結果: 1 String(obj) 解析: obj 型別 Object, 呼叫 ToPrimitive(obj, String) 用 String hint 呼叫 obj 的 [[DefaultValue]] 內部方法 呼叫 obj 的 toString 方法, 結果為 2 返回 ToString(2) 結果: '2' ``` ## 型別轉換的實際運用 ### 加號運算子 + lval + rval 運算流程如下: 1. 令 lprim 為 ToPrimitive(lval). 2. 令 rprim 為 ToPrimitive(rval). 3. 如果 Type(lprim) 為 String 或者 Type(rprim) 為 String,則: 返回由 ToString(lprim) 和 ToString(rprim) 連線而成的字串. 4. 返回將加法運算作用於 ToNumber(lprim) 和 ToNumber(rprim) 的結果. 例子: ```js ([] + {}) 解析: 1. lprim = ToPrimitive([]); // '' 2. rprim = ToPrimitive({}); // '[object Object]' 3. lprim 和 rprim 都為字串, 返回 ToString('') + ToString('[object Object]') // '[object Object]' 結果: '[object Object]' (undefined + {}) 解析: 1. lprim = ToPrimitive([]); // undefined; 2. rprim = ToPrimitive({}); // '[object Object]'; 3. Type(rprim) 為 String, 返回 ToString(undefined) + ToString('[object Object]') // 'undefined[object Object]' 結果: 'undefined[object Object]' (undefined + 1) 解析: 1. lprim = ToPrimitive([]); // undefined; 2. rprim = ToPrimitive(1); // 1; 3. Type(lprim) Type(rprim) 都不是 String; 4. 返回 ToNumber(undefined) + ToNumber(1) // NaN 結果: NaN ``` ### 其他運算子 - * / lval 和 rval 的 - * / 運算子流程如下: 1. 令 lnum 為 ToNumber(lval). 2. 令 rnum 為 ToNumber(rval). 3. 返回 ToNumber(lprim) 和 ToNumber(rprim) 運算的結果. ### 非嚴格相等比較
例子:
```
[] == {}
-> this == this
-> false
[] == 0
-> ToPrimitive([]) == 0
-> '' == 0
-> ToNumber('') == 0
-> 0 == 0
-> true
[0] == 0
-> ToPrimitive([0]) == 0
-> '0' == 0
-> ToNumber('0') == 0
-> 0 == 0
-> true
[11] == 11
-> true
['0'] == false
-> ToPrimitive(['0']) == ToNumber(false)
-> '0' == 0
-> 0 == 0
-> true
({} == '[object Object]')
-> ToPrimitive({}) == '[object Object]'
-> '[object Object]' == '[object Object]'
-> true
```
__參考__
* [ECMAScript5.1中文版](http://yanhaijing.com/es5/#201)
* [JavaScript 中的相等性判斷](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sa
物件的預設值由把 PreferredType 傳入作為hint引數呼叫物件的內部方法[[DefaultValue]]得到| #### 物件內部方法 [[DefaultValue]] (hint) 當用 String hint 呼叫 O 的 [[DefaultValue]] 內部方法,採用以下步驟: 1. str = O.toString(); 2. 如果str為原始值,返回str; 3. val = O.valueOf(); 4. 如果val為原始值,返回val; 5. 丟擲一個 TypeError 異常。 當用 Number hint 呼叫 O 的 [[DefaultValue]] 內部方法,採用以下步驟: 1. val = O.valueOf(); 2. 如果val為原始值,返回val; 3. str = O.toString(); 4. 如果str為原始值,返回str; 5. 丟擲一個 TypeError 異常。 __當不用 hint 呼叫 O 的 [[DefaultValue]] 內部方法時, 如果 O 為Date, 則hint=String, 除此之外, hit=Number。__ 例子: ```js ToPrimitive({}) 解析: 資料型別為 Object, 呼叫[[DefaultValue]] 沒有用 hint 呼叫, 則 hint 為 Number val = O.valueOf(); val 是 {}, 不是原始值 str = O.toString(); str 是 '[object Object]', 是原始值, 返回'[object Object]' 結果: '[object Object]' ``` ### ToNumber 轉為數字型別.(是
Number(value)
1. Call ToPrimitive(input argument, hint Number).
2. Call ToNumber(Result(1)).
3. Return Result(2).| ### ToString 轉為字串型別.(是
String(value)
如果引數是 false,那麼結果為 "false"。| |Number|將數字轉換為字串。| |String|結果等於輸入的引數(不轉換)。| |Object|Apply the following steps:
1. Call ToPrimitive(input argument, hint String).
2. Call ToString(Result(1)).
3. Return Result(2).| 例子: ```js var obj = { valueOf: function () { return 1; }, toString: function () { return 2; } } Number(obj) 解析: obj 型別 Object, 呼叫 ToPrimitive(obj, Number) 用 Number hint 呼叫 obj 的 [[DefaultValue]] 內部方法 呼叫 obj 的 valueOf 方法, 結果為 1 返回 ToNumber(1) 結果: 1 String(obj) 解析: obj 型別 Object, 呼叫 ToPrimitive(obj, String) 用 String hint 呼叫 obj 的 [[DefaultValue]] 內部方法 呼叫 obj 的 toString 方法, 結果為 2 返回 ToString(2) 結果: '2' ``` ## 型別轉換的實際運用 ### 加號運算子 + lval + rval 運算流程如下: 1. 令 lprim 為 ToPrimitive(lval). 2. 令 rprim 為 ToPrimitive(rval). 3. 如果 Type(lprim) 為 String 或者 Type(rprim) 為 String,則: 返回由 ToString(lprim) 和 ToString(rprim) 連線而成的字串. 4. 返回將加法運算作用於 ToNumber(lprim) 和 ToNumber(rprim) 的結果. 例子: ```js ([] + {}) 解析: 1. lprim = ToPrimitive([]); // '' 2. rprim = ToPrimitive({}); // '[object Object]' 3. lprim 和 rprim 都為字串, 返回 ToString('') + ToString('[object Object]') // '[object Object]' 結果: '[object Object]' (undefined + {}) 解析: 1. lprim = ToPrimitive([]); // undefined; 2. rprim = ToPrimitive({}); // '[object Object]'; 3. Type(rprim) 為 String, 返回 ToString(undefined) + ToString('[object Object]') // 'undefined[object Object]' 結果: 'undefined[object Object]' (undefined + 1) 解析: 1. lprim = ToPrimitive([]); // undefined; 2. rprim = ToPrimitive(1); // 1; 3. Type(lprim) Type(rprim) 都不是 String; 4. 返回 ToNumber(undefined) + ToNumber(1) // NaN 結果: NaN ``` ### 其他運算子 - * / lval 和 rval 的 - * / 運算子流程如下: 1. 令 lnum 為 ToNumber(lval). 2. 令 rnum 為 ToNumber(rval). 3. 返回 ToNumber(lprim) 和 ToNumber(rprim) 運算的結果. ### 非嚴格相等比較
被比較值 B | |||||||
---|---|---|---|---|---|---|---|
Undefined | Null | Number | String | Boolean | Object | ||
被比較值 A | Undefined | true |
true |
false |
false |
false |
IsFalsy(B) |
Null | true |
true |
false |
false |
false |
IsFalsy(B) |
|
Number | false |
false |
A === B |
A === ToNumber(B) |
A=== ToNumber(B) |
A== ToPrimitive(B) |
|
String | false |
false |
ToNumber(A) === B |
A === B |
ToNumber(A) === ToNumber(B) |
ToPrimitive(B) == A |
|
Boolean | false |
false |
ToNumber(A) === B |
ToNumber(A) === ToNumber(B) |
A === B |
ToNumber(A) == ToPrimitive(B) |
|
Object | false |
false |
ToPrimitive(A) == B |
ToPrimitive(A) == B |
ToPrimitive(A) == ToNumber(B) |
|