---
# System prepended metadata

title: JS 核心 7 - 寬鬆相等、嚴格相等以及隱含轉型
tags: [' 寬鬆相等、嚴格相等以及隱含轉型', JS 核心, ' Javascript']

---

---
title: 'JS 核心 7 - 寬鬆相等、嚴格相等以及隱含轉型'
tags: JS 核心, Javascript, 寬鬆相等、嚴格相等以及隱含轉型
description: 2021/02/03
---

JS 核心 -- 寬鬆相等、嚴格相等以及隱含轉型
===

## 嚴格相等 : === (嚴格相等)、!== (嚴格不相等)
**型別**和**數值**都要相等。 (不會替數值做自動轉型)
```
console.log( 1 === '1' ); // false
console.log( 1 === 1 );   // true
console.log( 1 !== '1' ); // true (嚴格相等否定)
```
以下是幾個值得特別注意的例子：  
NaN 不等於自己 ( NaN 不大於、不小於也不等於任何數字，所以也不等於它自己 )
```
console.log( NaN === NaN ); // false，兩個 (不是數值) 做比對
console.log( +0 === -0 );   // true，雖然值不同，但會回傳true
```

## 寬鬆相等 : == (寬鬆相等)、!= (寬鬆不相等)
值和型別**不**完全相同，可能會回傳true  

## ☞ 簡略規則

<span class="green">型別相同，就會以同一型做比較，但要注意</span>
1. NaN 不等於自己 ( NaN 不大於、不小於也不等於任何數字，所以也不等於它自己 )
2. +0、-0 彼此相等
3. 物件 (含 Function 和 Array ) 的相等是比較參考（reference），若參考相等才是相等

<span class="green">型別不同，將其中一個或兩個值先做強制轉型，再用型別相同的做比較</span>
1. 字串轉為**數字**
2. 布林轉為**數字**
3. **null 與 undefined** 在寬鬆相等下<span class="red">會強制轉型為彼此，不會轉為數字型別，</span>因此是相等的但不等於其他值
```
console.log( Number(null) , Number(undefined) );  // 0 NaN
console.log( null == 0 );　　     　 // false（null不會轉成數字做比對）
console.log( null == undefined );   // true 
console.log( null === undefined );  // false 
``` 

<span class="green">若比較的對象是物件，優先使用 valueOf(..)，再使用 toString(..) ，將物件取得基本型別的值，再做比較。</span>  
物件和非物件比對－<span class="red">**使用包裹物件轉換**</span>  
```
console.log( [10] );
console.log( Number([10]) );  // 10 (加上包裹物件後，得到數字)
console.log( 10 == [10] );    // true

console.log( ['A'] );
console.log( String(['A']) );  // A (加上包裹物件後，得到字串)
console.log( 'A' == ['A'] );   // true
```
物件和物件比對－<span class="red">**注意參考位置**</span> (比對的不是物件裡的值，而是他的參考位置)
```
console.log({} == {});  // false  (兩者參考位置不同)
console.log([] == []);  // false  (兩者參考位置不同)

var a = [];
var b = a;  // b 取得陣列 a 的參考位置
console.log( a == b );   // true  (a 和 b 都使用同一個參考路徑)
console.log( a === b );  // true
```


<span class="green">例外 : 0 代表 false ， !0 代表 true。</span>
```
console.log( 0 );           // 0     (又代表false)
console.log( !0 );          // true
console.log( Number(!0) );  // 1
console.log( '1' == !0 );   // true  (布林和字串比對，會自動轉型為數字)
```


✏ **範例 :** 數字和字串比對－字串自動轉為<span class="red">**數值**</span>
```
console.log( 1 == '1' );     // true  ( '1'自動轉為數值做比對 )
console.log( Number('1') );  // 1     (轉換字串為數值)

console.log( 17 == '0x11' );   // true  (0x 代表將數值轉為 16 進位，11 代表 16+1)
console.log( Number('0x11') ); // 17    (轉換字串為數值)
```

✏ **範例 :** 布林和字串比對－兩者都會自動轉為<span class="red">**數值**</span>  
(可參考下段 "轉型" 的筆記)
```
console.log( true == 'true' );  // false
console.log( Number('true') );  // NaN (字串無法轉為正常的數值)
console.log( Number(true) );    // 1
console.log( true == '1' );     // true
console.log( false == '0' );    // true
console.log( true == 1 );       // true
console.log( false == 0 );      // true
console.log( 1 != '1' );        // false (右邊字串 '1' 被轉型為數字 1)
```
✏ **範例 :** 純物件 - 使用 <span class="red">**String**</span> 做轉換
```
// 通常不會這樣做比對，僅供參考
console.log( { A : 'A' } );           // { A : 'A' }
console.log( String({ A : 'A' }) );   // [object Object]
console.log( '[object Object]' == { A : 'A' } );  // true
```









## 轉型
### ToString : 任何非字串的值被強制轉型為字串，規則說明如下
* undefined ->'undefined'
* null -> 'null'
* boolean 的 true -> 'true'、false -> 'false'
* number -> '1.234'
* object  

若有定義其 toString 方法，則會以它自己的 toString 方法所產生的結果為優先
```
// 陣列有自己定義的 toString 方法
[1,2,3].toString();  // "1,2,3"
```
若沒有定義 toString 方法，則回傳內部的屬性 [[Class]] ，
這是一個用來標記這個值是屬於物件的哪個子分類的標籤
```
({}).toString()  // [object Object]
```

### ToNumber : 將非數字值當成數字來操作，規則說明如下
* undefined -> NaN
* null -> +0 即是 0
* boolean 的 true -> 1、false -> +0 即是 0
* number -> 不轉換
* string -> 數字或 NaN，結果與 Number ( input ) 是一樣的
* object -> 若有定義其 valueOf 方法，會優先使用 valueOf 取得其原始型別值，反之，若沒有定義 valueOf 方法，則改用 toString 方法取得其原始型別值，再用 Number(..) 轉為數字。

### ToBoolean
Truthy 與 Falsy 的概念，在 JavaScript 中會是 **false 值**如下
* "" ‒ 空字串
* 0、 -0、 NaN
* null
* undefined
* false  

除了以上之外，其餘都會被轉為 true，但其中比較特殊的如下
* [ ]
* { }
* function foo ( ) { } ‒ 函式
* '0' ‒ 文字的 0
* ' ' ‒ 包含空白的引號

### 強制轉型
```
// 使用內建函式：
String(123)           // "123"
Number('123')         // 123
-----------------------------
// 使用物件原型方法：
(123).toString()      // "123"
-----------------------------
// 使用一元正/負運算子：
+('123’) / -('-123')  // 123 / 123
```
### 隱含轉型 : 程式碼中某個型別轉換動作是沒有明確指出要轉換型，但卻有轉型的動作
☞ **物件與非物件** : 會透過包裹物件來進行型別轉換，最後再執行比對動作。  

陣列 [10] 為物件，當數字與物件進行寬鬆比對時，先操作將物件轉換成數字，最後再進行比對
```
console.log( 10 == [10] );  // true
```
陣列 [‘A’] 為物件，當字串與物件進行寬鬆比對時，先操作將物件轉換成字串，最後再進行比對
```
console.log( 'A' == ['A'] );  // true
```




## :memo: 學習回顧  
:::info
* 嚴格相等、寬鬆相等的差異是在做**值的比較**時是否會做**強制轉型**
* 型別相同，就會以同一型做比較
    * NaN 不等於自己 ( NaN 不大於、不小於也不等於任何數字，所以也不等於它自己 )，console.log( NaN === NaN ); // false
    * +0、-0 彼此相等，console.log( +0 === -0 ); // true，
* 型別不同，將其中一個或兩個值先做強制轉型，再用型別相同的做比較
    * 字串轉為**數字**
    * 布林轉為**數字**
    * 布林和字串比對，兩者都轉為**數字**
* **null 與 undefined** 在寬鬆相等下<span class="red">會強制轉型為彼此，不會轉為數字型別，</span>因此是相等的但不等於其他值
    * console.log( Number(null) , Number(undefined) );  // 0 NaN
    * console.log( null == 0 );　　     　 // false（null不會轉成數字做比對）
    * console.log( null == undefined );   // true
    * console.log( null === undefined );  // false  
* 物件和非物件比對－使用包裹物件轉換
* 物件和物件比對－比對的不是物件裡的值，而是他的參考位置
* 例外 : 0 代表 false ， !0 代表 true
    * console.log( !0 );          // true
    * console.log( Number(!0) );  // 1
:::

## :+1: 相關參考文件 
:::info
[桑莫 - 強制轉型](https://cythilya.github.io/2018/10/15/coercion/#%E6%98%8E%E7%A2%BA%E7%9A%84%E5%BC%B7%E5%88%B6%E8%BD%89%E5%9E%8Bexplicit-coercion)  
[強制轉型、註解、變數、範疇](https://www.coderbridge.com/series/9e5162da940f473a9f1cfeece124ee98/posts/f4a2d1e05f1c4a29a459f9cf0591d026)  
[kuro - 「比較」與自動轉型的規則](https://ithelp.ithome.com.tw/articles/10191254)  
[kuro - 運算式與運算子](https://ithelp.ithome.com.tw/articles/10191180)  
[包裹物件、自動轉型](https://jimmywei01.github.io/2019/05/28/JS-%E7%B4%80%E9%8C%842-%E5%8C%85%E8%A3%B9%E7%89%A9%E4%BB%B6%E3%80%81%E8%87%AA%E5%8B%95%E8%BD%89%E5%9E%8B/)  
[包裹物件、強制轉型](https://medium.com/j51217123/js-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-%E5%8C%85%E8%A3%B9%E7%89%A9%E4%BB%B6-%E5%BC%B7%E5%88%B6%E8%BD%89%E5%9E%8B-13ff82fea540)  
:::

<style>
.red {
  color: red;
}
.green {
  color: green;
}
</style>