---
# System prepended metadata

title: 尋找是否包含某元素：includes、indexOf
tags: [' JavaScript']

---

---
tags: 如何正確使用字串, 字串 or 陣列處理方法, string, 陣列, array, indexOf, lastIndexOf, includes, 嚴格相等比較, SameValueZero, SameValueNonNumeric, searchElement, search
---

尋找是否包含某元素：includes、indexOf
===

:::spoiler 目錄
- includes、indexOf
- indexOf
	- 參數
	- 範例
	- 搜索空字串
	- 參數 position 是負值
		- 文字
		- 陣列
	- 從後面往前找：lastIndexOf
		- 參數
        - 範例
        - 與 indexOf 的不同
    - search
- includes
	- 範例
- indexOf 與 includes 的差異
	- 回傳值
	- 關於 NaN 的問題
	- 關於空陣列的問題
- 嚴格相等比較 vs. SameValueZero
	- 嚴格相等比較
	- SameValueZero
- 元素型別為 Object 時，需要考慮值的參考
:::

<br/>

返回新的值，++不會更改原始的值++。

includes、indexOf
---

|  | 過去 | ES7 |
| -------- | -------- | -------- |
| 尋找是否包含某元素 | ++indexOf++ | ++includes++ |
|  | ++lastIndexOf++ |  |

尋找陣列中是否包含某元素，過去使用 indexOf 處理，現在 ES7 有 includes 可以使用。

假若比對的是字串，可以使用 match、search，詳細見：[尋找比對字串：match、search](https://hackmd.io/wk6ELp3pTSujIwf8yDlFjQ?view)。

indexOf
---
回傳元素於 **陣列 / 字串** 中第一個被找到之**索引**，若不存在於陣列 / 字串中則回傳 -1。

### Ⅰ. 參數

> indexOf ( searchString [ , fromIndex] )

- searchElement：選填，判斷的值，預設為 `undefined`。
- fromIndex：選填，從哪個位置開始判斷，預設為 0。

:::spoiler 如果<u>未搜尋成功，返回 -1</u>。
```jsx
"0123".indexOf("A"); // -1
"0123".indexOf("1",99); // -1

[2, 9, 9].indexOf(7); // -1
[2, 9, 9].indexOf(2, 1); // -1
```
:::

:::spoiler 參數 searchString <u>沒有指定，默認搜尋的是 `undefined`</u>。
```jsx
"0123".indexOf(); // -1
'undefined'.indexOf(); // 0

[2, 9, 9].indexOf(); // -1
[undefined].indexOf(); // 0
```
:::

:::spoiler 參數 searchString 區分大小寫。
```jsx
let string = "Hello World!";
string.indexOf("h"); // -1 
string.indexOf("H"); // 0 
```
:::

### Ⅱ. 範例

```jsx
let string = "Hello World!";
string.indexOf("!"); // 11
string.indexOf("!",11); // 11
```
```jsx
let array = [2, 9, 9];
array.indexOf(2);  // 0
array.indexOf(9, 2);  // 2
```

> [Dome](https://codepen.io/betty-hu/pen/NWXeJeV)

### Ⅲ. 搜索空字串

搜索空字串，假若 fromIndex 參數小於字串長度，結果將是 fromIndex 的值。反之，將會是字串長度。

:::spoiler 沒有 fromIndex 參數的情況，返回 0。
```jsx
"0123".indexOf(""); // 0
```
:::

:::spoiler 參數 fromIndex 小於字串長度，返回 fromIndex 的值。
```jsx
"0123".indexOf("",1); // 1
```
▲ ( fromIndex 參數 = 1 ) < (字串長度 = 4) → 返回 fromIndex 的值 = 1
:::

:::spoiler 參數 fromIndex 大於字串長度，返回字串長度。
```jsx
"0123".indexOf("",99); // 4
```
▲ ( fromIndex 參數 = 99 ) > (字串長度 = 4) → 返回字串長度 = 4
:::

:::spoiler 假若對象是陣列的話，返回 -1。
```jsx
[0,1,2,3].indexOf("",1); // -1
```
:::

### Ⅳ. 參數 fromIndex 是負值

#### Ⅳ-1. 文字
參數 fromIndex 是負值，假若是文字，將會視作為 0，也就是搜索全部。

```jsx
"Hello World!".indexOf("World",-1); // 6
```
#### Ⅳ-2. 陣列
參數 fromIndex 是負值，假若是陣列，從後方往前計算。

最後一個的索引值是 -1，也就是說 fromIndex = -1，等於最後一項，以此類推。

```jsx
["Hello", "World", "!"].indexOf("!",-1); // 2

["Hello", "World", "!"].indexOf("World",-1); // -1
["Hello", "World", "!"].indexOf("World",-2); // 1
```

### Ⅴ. 從後面往前找：lastIndexOf
與 indexOf 相同，但是從後面往前找。

#### Ⅰ.參數
> lastIndexOf ( searchElement [ , fromIndex ] )

- searchElement：選填，判斷的值，預設為 `undefined`。
- fromIndex：選填，從哪個位置開始判斷，預設為最後一項。

#### Ⅱ. 範例

```jsx
"Hello World!".lastIndexOf("!"); // 11
"Hello World!".lastIndexOf("!",10); // -1
```

> [Dome](https://codepen.io/betty-hu/pen/bGQOMLV?editors=0012#_=_)

#### Ⅲ. 與 indexOf 的不同

```jsx
"abcabc".lastIndexOf("a"); // 3
"abcabc".lastIndexOf("a",5); // 3

"abcabc".indexOf("a"); // 0
"abcabc".lastIndexOf("a",0); // 0
```
▲ fromIndex 預設最後一項。
```jsx
"abcabc".indexOf(""); // 0
"abcabc".lastIndexOf(""); // 6
```
▲ 搜索空字串：沒有 fromIndex 參數的情況，返回字串長度。
```jsx
"abcabc".indexOf("c",-3); // 2 （負數視作搜尋全部）
"abcabc".lastIndexOf("c",-3); // -1
```
▲ 參數 fromIndex 是負值，假若是文字，將會回傳 -1。

### Ⅵ. search
> str . search ( regexp )

```jsx
/* 沒有使用正規表達式，僅使用字串搜尋 */
'some text'.search('text') // 0
'some text'.search('TEXT') // -1

/* 使用正規表達式 */
'some text'.search(/TEXT/i) // 0
```
找出字串在另一字串中的位置，有找到回傳字串的起始位置，沒找到回傳 -1。

跟 `indexOf()` 相同都是尋找並回傳位置，只是 `search()` 可以用正規表示式當參數，且只能作用在字串上，不能用於陣列。詳細見：[尋找比對字串：match、search](https://hackmd.io/wk6ELp3pTSujIwf8yDlFjQ?view)


includes
---
ES7 提供的新函式。直接回傳是否包含該元素的 Boolean 值，true 代表包含、false 代表不包含。

#### 參數
> includes ( searchElement [ , fromIndex] )

- searchElement：選填，判斷的值，預設為 undefined。
- fromIndex：選填，從哪個位置開始判斷，預設為 0。

用法同 indexOf。


#### Ⅰ. 範例

```jsx
'JS ECMA TC39'.includes('ECMA'); // true
['JS', 'ECMA', 'TC39'].includes('ECMA'); // true
['JS ECMA TC39'].includes('ECMA'); // false

/* searchElement 預設 */
"undefined".includes(); // true
[undefined].includes(); // true

/* fromIndex 參數 */
"abcdef".includes("c",3); // false
"abcdef".includes("c",-3); // true
['JS', 'ECMA', 'TC39'].includes('ECMA', 1); // true
['JS', 'ECMA', 'TC39'].includes('ECMA', -1); // false

/* 搜索空字串 */
"abcdef".includes(""); // true
[00,11,22].includes(""); // false
```

> [Dome](https://codepen.io/betty-hu/pen/zYMeRRM?editors=0012)


indexOf 與 includes 的差異
---

### 回傳值
- indexOf：回傳元素於 陣列 / 字串 中第一個被找到之索引。
- includes：回傳是否包含該元素的 Boolean 值。

```jsx
['JS', 'ECMA', 'TC39'].indexOf('ECMA'); // 1
['JS', 'ECMA', 'TC39'].indexOf('ECMA') == true  // true
['JS', 'ECMA', 'TC39'].indexOf('ECMA') !== -1  // true

['JS', 'ECMA', 'TC39'].includes('ECMA'); // true
```

### 關於 NaN 的問題
- indexOf：使用嚴格相等比較，而 NaN !== NaN，無法在陣列中找到 NaN。
- includes：使用 searchElement 演算法，可以解決 NaN 的問題。

```jsx
[NaN].indexOf(NaN) !== -1 // false
[NaN].includes(NaN) // true
```

### 關於空陣列的問題
- indexOf：遇到空陣列會直接跳過，繼續比對下個元素，無法在陣列中找到空陣列。
- includes：不會跳過空陣列，而是視為 `undefined`，因此能夠捕抓空陣列。
```jsx
// Array(3) = [ , , ]
Array(3).indexOf() !== -1 // false
Array(3).includes() // true
```

嚴格相等比較 vs. SameValueZero
---
indexOf 採用嚴格相等比較；includes 採用 SameValueZero。

### ++嚴格相等比較++
1. 比較型別是否相同，不同就回傳 false。
2. 型別是 Number 或 BigInt ，**進行值的比較**。在值的比較中，++值是 NaN 回傳 false，這是 indexOf 中 NaN 不等於 NaN 的原因++。 
3. 其他型別使用 SameValueNonNumeric 演算法。 

:::spoiler 補充：SameValueNonNumeric 演算法
- 值為 null 或 undefined，回傳 true。
- String、Boolean、Symbol、Object 型別比較值是否相同。
:::
        
### ++SameValueZero++

1. 比較型別是否相同，不同就回傳 false。
2. 型別是 Number 或 BigInt ，**進行 Type(x)::sameValueZero(x, y) 的比較**。++兩方的值是 NaN，回傳 true，這是 includes 能夠解決 NaN 不等於 NaN 的原因++。
3. 其他型別使用 SameValueNonNumeric 演算法。

:::spoiler 補充：`Type(x)::sameValueZero(x, y)`
- `Type(x)::sameValueZero(x, y)`：每種內建型別定義許多 operations，而 Number、BigInt 是 ECMAScript 內建的 Numeric 型別，其中的 operations 是 T::sameValueZero(x, y)。
- 兩方的值是 NaN，回傳 true，這是 includes 能夠解決 NaN 不等於 NaN 的原因。
:::

元素型別為 Object 時，需要考慮值的參考
---

元素型別為 Object，值相同但參考不同，indexOf 和 includes 回傳 false。
```jsx
let array = [{id: 1}, {id: 2}, {id: 3}];

/* 值相同，但參考不同 */
array.indexOf({id: 2}) !== -1  // false
array.includes({id: 2})        // false
```
```jsx
let item = {id: 2};
let array = [{id: 1}, item, {id: 3}];

/* 值相同、參考相同 */
array.indexOf(item) !== -1  // true
array.includes(item)        // true
```