iT邦幫忙

2022 iThome 鐵人賽

DAY 17
1

Abstract

整篇會分成以下幾個部分:

  • 使用時機
  • 語法
  • 說明
  • 範例
  • 注意事項
  • ECMAScript
  • 結論

includes() 這個 method 的全寫應該是 Array.prototype.includes,有興趣可以看 Day 2 的介紹,這邊會直接使用 includes() 作為替代。

範例的 callback 都會使用箭頭函式做介紹,如果尚不熟悉的話可以參考 MDN 的介紹。

最後會透過分析 ECMAScript 來驗證是否有吻合,如果覺得 ECMAScript 有點艱澀難懂,我們在 Day 4 、Day 5 有介紹其相關術語可以幫助閱讀。


使用時機

當你想要知道這個陣列是否包含某個值時。

includes 會回傳一個 truefalse 告訴你答案。

它可以做到 2 個 indexOf() 做不到的事,查找 NaN 跟 empty slot (不存在的陣列元素)。


語法

includes(searchElement)

includes(searchElement, fromIndex)

參數

第 1 個參數為 searchElement

第 2 個參數為 fromIndex

searchElement

想要確認的元素。

fromIndex

搜索的起始點,如果不提供則預設為 0。

如果值為負數則會將其加上陣列長度 (fromIndex + array.length)。

如果值為小數則會將其轉成整數。

如果 fromIndex 大於等於陣列長度 includes 會回傳 false

Return Value

如果陣列有包含 searchElement 回傳 true,否則回傳 *false

Mutability

不會變動到原陣列。


說明

includes() 會去判斷 searchElement 是否跟陣列的每個元素相等,只要有一個相等,便會回傳 true 否則回傳 false

比較的方式有幾點需要注意:

  • NaNNaN 會相等 (表示它可以搜尋 NaN 是否存在)
  • -00+0 彼此相等
  • 字串是大小寫敏感 (表示如果大小寫沒有完全符合便不會匹配)

可以利用 fromIndex 來控制搜索的區間,它可是一個正負數 (包含小數),如果不提供則預設為 0,也就表示整個陣列都會被搜索。

indcludes() 可以用在稀疏陣列上 (sparse array),其中的 empty slot 會被視作 undefined


範例

Example 1 - 基礎用法

const array = [0, 'Pedro', NaN, , '2', 3]

console.log(array.includes('Pedro'))
// true
console.log(array.includes(-0))
// true
console.log(array.includes(NaN))
// true
console.log(array.includes(undefined))
// true
console.log(array.includes('peDro'))
// false
console.log(array.includes(2))
// false

注意 includes() 可以查找 NaN,且 empty slot 也不會被忽略。

Example 2 - 提供 fromIndex

const array = [1, 2, 3, 4, 5]


console.log(array.includes(1, 6))
// false
console.log(array.includes(1, -6))
// true
console.log(array.includes(1, 2))
// false
console.log(array.includes(3, -5))
// true
console.log(array.includes(3, -2))
// false

fromIndex 大於陣列長度,則不會執行匹配,直接回傳 false

fromIndex 為負數且加上陣列長度仍小於 0 的話會被視為 0。

Example 3 - 利用 includes() + reduce() 過濾重複元素

const array = [1, 2, 3, 4, 5, 1, 2 ,3, 4, 5]

const uniqueArray = array.reduce((unique, element) => unique.includes(element) ? unique : [...unique, element], [])

console.log(uniqueArray)
// [1, 2, 3, 4, 5]

這邊利用 reduce() 來遍歷陣列並帶入一個新的空陣列。

如果新的陣列不包含當前元素便將其推入後往下傳遞,如果已經包含便直接往下傳遞。


注意事項

如果給予 fromIndex 負值表示起始點要從陣列結尾開始計算,這不代表要反向搜索,只是會從陣列結尾計算起始點的索引。

includes() 是大小寫敏感的,如欲搜索的 searchElement 為字串,則必須要完整符合才會匹配。

includes() 可以使用小數,因為它會先被轉成整數,但請注意它被轉換的方式可能跟預期不同,可參考下方 ECMAScript 的說明。

indexOf() 無法完全取代 includes() 的用途,因為它沒有辦法匹配 NaN 跟 empty slot。


ECMAScript

17.1

includes() 的演算法並沒有要求呼叫它的一定要是一個陣列,可以從步驟 1 跟 Note 2 得知,為了方便解釋,這邊一律使用陣列來說明。

演算法的前 2 個步驟都是用來做一些前置處理,包括轉型、確認長度。

步驟 3 可以看到如果陣列長度為 0,則直接回傳 false 表示不會做任何的匹配。

步驟 4 蠻值得注意的,這個 ToIntegerOrInfinityindex 是小數仍然可以正常使用,但因為它在轉換的過程使用了絕對值,導致轉成整數的結果可能會跟預期的不一樣。

步驟 5 挺有趣的,如果 fromIndexundefined,它會被步驟 4 的 ToIntegerOrInfinity 先轉換成 NaN,然後回傳 0 出來,因此它才會直接斷言 (assert) n 會是 0,這也驗證了不提供此參數的話預設會是 0,我們來驗證一下:

17.2

步驟 9 驗證了如果 fromIndex 為負數會將其加上陣列的長度。

步驟 10 便開始遍歷陣列元素做比對,可以看到當 searchElement 一跟陣列當前的元素比對成功便馬上終止 includes() 並回傳 true,這也驗證了只要找到第一個匹配的元素便不會再往下找。

這邊有一個很值得注意的是 SameValueZero(x, y),它並不是 indexOf 使用的嚴格相等 (===),但它們同樣會在步驟 1 剔除型別不相等的值,接著再把 Number、非 Number fen分開來處理:

  • Number::sameValueZero(x, y) - 從其演算法可以看出 NaN 會相等,-00+0 彼此會相等。
  • SameValueNonNumber(x,y) - 其步驟 1 直接斷言 (Assert) x, y 的型別相等,表示傳進去的參數應該要是 2 個型別相同的值,接著需要特別注意的便是物件型別,它比較的會是物件的參照 (reference) 而非內容!

出現 ? 的地方代表有可能會丟出錯誤,所以整個演算法有 4 處有機會丟出錯誤,例如步驟 4 的 ToIntegerOrInfinity(),當你傳入 Symbol 或 BigInt 即會從內部的 ToNumber()丟出一個 TypeError 的錯誤,我們來驗證一下:

17.3

如果出現 ! ,則代表這個 abstract operation 絕對不會丟出錯誤,例如步驟 10 的 ToString() 它會在參數是一個 Symbol 時丟出一個 TypeError,但我們確定丟進去的是一個 Number (F(k)),因此不會有丟出錯誤的可能。

最後我們再來驗證一下 includes() 是否真的被做成通用的:

17.4

從 ECMAScript 的演算法來看,尚未找到與 JavaScript 實作的不同之處。


結論

大多數人可能不知道為什麼特別需要 includes(),因為 indexOf() 就可以做到一樣的事情,但他們除了字面語義的差別外,includes() 還可以查找 NaN 跟 empty slot,這是 ECMAScript 將它們刻意做出的分別。

最後,希望大家可以開心地使用各種咩色,體驗它帶給你的便利,祝大家歸剛沒煩惱。


參考資源


上一篇
Day16 咩色用得好 - Array.prototype.indexOf
下一篇
Day 18 咩色用得好 - Array.prototype.flat
系列文
咩色用得好,歸剛沒煩惱 - 從 ECMAScript 偷窺 JavaScript Array method30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言