整篇會分成以下幾個部分:
find
這個 method 的全寫應該是 Array.prototype.find
,有興趣可以看 Day 2 的介紹,這邊會直接使用 find()
作為替代。
Array method 有不少會使用到 callback function,如果尚不熟悉的話,可以看 Day 2 的介紹
範例使用的 callback 都會使用箭頭函式做介紹,如果尚不熟悉的話可以參考 MDN 的介紹。
最後會透過分析 ECMAScript 來驗證是否有吻合,如果覺得 ECMAScript 有點艱澀難懂,我們在 Day 4 、Day 5 有介紹其相關術語可以幫助閱讀。
當你需要從陣列找 1 個 element 的時候。
find()
會回傳第 1 個符合條件的元素,如果沒有則回傳 undefined
。
你同時可能會想到的其他方法:
findLast()
- 從陣列結尾開始,找到第 1 個符合條件的 elementfilter()
- 回傳一個包含所有符合條件的元素陣列includes()
- 確認元素是否有在陣列內indexOf()
- 回傳元素的索引值findIndex()
- 回傳元素的索引值some()
- 測試陣列的元素是否至少有一個符合條件find((element, index, array) => {
/* 測試條件 */
})
find(callbackFn,thisArg)
find(function(element, index, array) {
/* 測試條件 */
}, thisArg)
find()
的第 1 個參數為 callback, 第 2 個參數為選擇性 (optional) 的 thisArg
。
callback
這個 callback 又稱為 testing function 或 predicate (規範用語),顧名思義它會被拿來測試某些條件,再準確一點應該稱作斷言 (assert) ,因為它最後回傳的值會被強制轉換成布林值 (true 或 false)!
當這個 callback 被呼叫時會帶入 element
、index
、array
三個參數。
find()
會按照陣列元素的順序依次 (升冪) 呼叫這個 callback,直到這個 callback 回傳 true 或當陣列元素已被遍歷完畢即停止,換句話說,如果這個陣列有 5 個元素,那這個 callback 至多會被呼叫 5 次。
element
陣列當前的元素 (element),callback 的第 1 個參數,為 find()
當前遍歷到的元素,也就表示 element
會依陣列的順序動態變化。
index
陣列當前元素的索引值 (index),callback 的第 2 個參數,為 find()
當前遍歷到的元素其索引值,也就表示 index
會依陣列的順序動態變化。
array
呼叫 find()
的陣列本身 (被遍歷的陣列本身), callback 的第 3 個參數,不論 find()
當下遍歷到哪個元素上, array
都會指向被遍歷的陣列本身,也就是呼叫 find()
的陣列本身。
thisArg
為 find()
的第 2 個選擇性參數,它會被傳入 callback 並作為其 this
的值,否則就會是 undefined
。
請注意,如果 callback 使用箭頭函式的話則沒有作用!
可以參考 Example - 4 的寫法。
回傳陣列中第一個被找到的元素,也就是陣列中第一個符合 testing function 的元素 (回傳 true 時),如果沒有找到則回傳 undefined
。
不會變動到原陣列。
const methods = ["map", "filter", "reduce", "find"];
const pickedMethod = methods.find(method => method === "reduce");
console.log(pickedMethod)
// "reduce"
methods
methods
呼叫 find()
時會順著原型鏈拿到放在 Array.prototype
指向的 prototype 物件上的 find()
methodfind()
呼叫時需傳入了一個 callback (testing function),因此我們直接在 find()
的參數裡定義一個帶有 method
參數的箭頭函式find()
被呼叫時, find()
會執行 callback,並帶入 element
、index
、array
等 3 個參數,其中的 element
會被指派給 method
這個參數"reduce"
"reduce"
就回傳 true
,否則回傳 false
"reduce"
即被找到 (回傳 true),因此 find()
便立即中止並將當前元素 ("reduce"
) 回傳出去"reduce"
被指派給了 pickedMethod
index
參數:const names = ['Tyler', 'Coby', 'Kingsley', 'Amber', 'Emma', 'Pedro']
const pickedName = names.find((name, index) => name.length < 5 && index > 1);
console.log(pickedName)
// "Emma"
index
參數, callback 必須依照順序定義參數,不能略過前面的參數const actors = [
{ name: "Denzel Washington", age: 67 },
{ name: "Tom Hardy", age: 45 },
{ name: "Brad pitt", age: 58 },
{ name: "Michael Fassbender", age: 45 },
{ name: "Jake Gyllenhaal", age: 41 },
{ name: "Collin Farrel", age: 46 }
]
const result = actors.find((actor, index) => actor.age >= 45 && index > 2)
console.log(result)
// { name: "Michael Fassbender", age: 45}
thisArg
參數:const stars = ["Charlize", "Emma", "Jake", "Collin"]
const ages = [35, 16, 20, 41]
const childStar = stars.find(function(_star, index) {
return this[index] < 18;
}, ages)
console.log(childStar)
// Emma
ages
被作為 find()
的第 2 個參數傳入this
會指向 ages
請注意 callback 定義時的參數順序,依序應為 element
、index
、array
,但不可隨意略過,假如你只想使用 index
而不需要 element
, 應該要照上面 Example 2 的寫法。
參數名稱可以隨意命名但通常會有一些慣例以增加可讀性,例如上面的例子便使用 method
來表示 methods
這個陣列的每個元素
這邊值得注意的是,雖然 find()
不會變動到原陣列,但我們傳進去的 callback 卻有可能 ,而陣列元素被遍歷的範圍在第一次呼叫 callback 前就已經確立好了 (也就是 find()
被呼叫後但 callback 尚未被呼叫),因此有可能會發生以下的狀況:
undefined
上述這種高併發 (concurrent) 的更動會導致程式碼非常難以閱讀,非常不建議使用 (除非有特殊的情境)。
ECMA 連結 - Array.prototype.find - ECMAScript
語法 - Array.prototype.find(predicate[,thisArg])
this
轉型成一個 object 後指派給 O
O
的長度並指派給 len
predicate
是不可呼叫的 object 則丟出一個 TypeErrork
k
< len
時,重複以下步驟Pk
O
物件的屬性 Pk
的值並指派給 kValue
predicate
並帶入 « kValue, ?(k), O »
這個 argumentList,並將 predicate
回傳的值轉成布林值後指派給 testResult
testResult
為 true,便回傳 kValue
k
= k
+ 1undefined
find()
的物件必須是一個陣列,從步驟 1 跟 Note 2 可以看得出來predicate
這個 callback, 並帶入被取出的 元素、當前計數、陣列本身 這 3 個參數undefined
?
, 代表有 4 處的 abstract operation 有機會丟出錯誤predicate
回傳的值都會被轉成布林值,也是變相告訴我們應該直接回傳布林值!
,代表這個這個 abstract operation 絕對不會丟出錯誤find()
是一個很常用的咩色,找資料很方便,你只需要將邏輯專注在這個 testing funciton 上並注意使用的參數即可。
thisArg
雖然挺方便的,但它在閱讀時有些不直覺,容易搞不清楚 callback 的 this
是指誰。
最後,希望大家可以開心地使用各種咩色,體驗它帶給你的便利,祝大家歸剛沒煩惱。
Array.prototype.find - MDN
Draft ECMA 262 - TC39