แรงบันดาลใจจากการเขียนคำตอบนี้ฉันจึงขยายและเขียนโพสต์บล็อกในภายหลังโดยละเอียด ฉันขอแนะนำให้ตรวจสอบสิ่งนั้นหากคุณต้องการพัฒนาความเข้าใจที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับวิธีคิดเกี่ยวกับปัญหานี้ - ฉันพยายามอธิบายทีละชิ้นและให้เปรียบเทียบ JSperf ในตอนท้ายโดยคำนึงถึงความเร็ว
ที่กล่าวว่าtl; dr คือสิ่งนี้: เพื่อให้บรรลุสิ่งที่คุณต้องการ (การกรองและการทำแผนที่ภายในการเรียกใช้ฟังก์ชันเดียว) คุณจะใช้Array.reduce()
ไฟล์.
อย่างไรก็ตามยิ่งอ่านได้มากขึ้น และ (สำคัญน้อยกว่า) มักจะเร็วกว่าอย่างมีนัยสำคัญ2แนวทางคือเพียงใช้ตัวกรองและแผนที่ผูกติดกัน:
[1,2,3].filter(num => num > 2).map(num => num * 2)
สิ่งต่อไปนี้คือคำอธิบายวิธีการArray.reduce()
ทำงานและวิธีใช้เพื่อกรองและแมปให้สำเร็จในการทำซ้ำ อีกครั้งหากสิ่งนี้ย่อเกินไปฉันขอแนะนำอย่างยิ่งให้ดูบล็อกโพสต์ที่เชื่อมโยงด้านบนซึ่งเป็นบทนำที่เป็นมิตรมากขึ้นพร้อมตัวอย่างที่ชัดเจนและความก้าวหน้า
คุณให้ลดอาร์กิวเมนต์ที่เป็นฟังก์ชัน (โดยปกติจะไม่ระบุชื่อ)
ฟังก์ชันที่ไม่ระบุตัวตนนั้นใช้พารามิเตอร์สองตัว - หนึ่ง (เช่นฟังก์ชันที่ไม่ระบุชื่อที่ส่งผ่านไปยังแผนที่ / ตัวกรอง / forEach) เป็นตัวดำเนินการ มีอาร์กิวเมนต์อื่นสำหรับฟังก์ชันที่ไม่ระบุชื่อที่ส่งผ่านเพื่อลดอย่างไรก็ตามฟังก์ชันเหล่านั้นไม่ยอมรับและนั่นคือค่าที่จะส่งต่อระหว่างการเรียกใช้ฟังก์ชันซึ่งมักเรียกกันว่าบันทึกบันทึกช่วยจำ
โปรดทราบว่าในขณะที่ Array.filter () ใช้เพียงอาร์กิวเมนต์เดียว (ฟังก์ชัน) Array.reduce () ยังใช้อาร์กิวเมนต์ที่สองที่สำคัญ (แม้ว่าจะเป็นทางเลือก) ซึ่งเป็นค่าเริ่มต้นสำหรับ 'บันทึก' ที่จะถูกส่งไปยังฟังก์ชันที่ไม่ระบุชื่อนั้นเป็นของ อาร์กิวเมนต์แรกและต่อมาสามารถกลายพันธุ์และส่งต่อระหว่างการเรียกใช้ฟังก์ชัน (หากไม่ได้ให้มา 'บันทึก' ในการเรียกใช้ฟังก์ชันที่ไม่ระบุชื่อตัวแรกโดยค่าเริ่มต้นจะเป็นตัวทำซ้ำตัวแรกและอาร์กิวเมนต์ 'iteratee' จะเป็นค่าที่สองในอาร์เรย์)
ในกรณีของเราเราจะส่งอาร์เรย์ว่างเปล่าเพื่อเริ่มต้นจากนั้นเลือกว่าจะฉีด iteratee ของเราลงในอาร์เรย์ของเราหรือไม่ตามฟังก์ชันของเรา - นี่คือกระบวนการกรอง
สุดท้ายเราจะส่งคืน 'อาร์เรย์ที่อยู่ระหว่างดำเนินการ' ของเราในการเรียกใช้ฟังก์ชันแบบไม่ระบุตัวตนแต่ละครั้งและการลดจะใช้ค่าส่งคืนนั้นและส่งเป็นอาร์กิวเมนต์ (เรียกว่าบันทึก) ไปยังการเรียกฟังก์ชันถัดไป
สิ่งนี้ช่วยให้ตัวกรองและแผนที่สามารถเกิดขึ้นได้ในการวนซ้ำครั้งเดียวลดจำนวนการทำซ้ำที่ต้องการลงครึ่งหนึ่ง - เพียงแค่ทำงานซ้ำสองครั้งในแต่ละครั้งดังนั้นจึงไม่มีอะไรบันทึกไว้นอกจากการเรียกใช้ฟังก์ชันซึ่งมีราคาไม่แพงนักในจาวาสคริปต์ .
สำหรับคำอธิบายที่สมบูรณ์ยิ่งขึ้นโปรดดูเอกสารMDN (หรือโพสต์ของฉันที่อ้างถึงในตอนต้นของคำตอบนี้)
ตัวอย่างพื้นฐานของการโทรลด:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
if (iteratee > 1) {
memo.push(iteratee * 2);
}
return memo;
}, initialMemo)
console.log(array)
รุ่นที่รวบรัดมากขึ้น:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
สังเกตว่า iteratee แรกไม่มากกว่าหนึ่งและถูกกรอง สังเกต initialMemo ด้วยซึ่งตั้งชื่อเพื่อให้การมีอยู่ของมันชัดเจนและดึงดูดความสนใจไปที่มัน อีกครั้งจะถูกส่งเป็น 'บันทึก' ไปยังการเรียกใช้ฟังก์ชันที่ไม่ระบุชื่อครั้งแรกจากนั้นค่าที่ส่งคืนของฟังก์ชันที่ไม่ระบุชื่อจะถูกส่งเป็นอาร์กิวเมนต์ 'บันทึก' ไปยังฟังก์ชันถัดไป
อีกตัวอย่างหนึ่งของกรณีการใช้งานแบบคลาสสิกสำหรับบันทึกช่วยจำจะส่งคืนตัวเลขที่น้อยที่สุดหรือมากที่สุดในอาร์เรย์ ตัวอย่าง:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
ตัวอย่างวิธีเขียนฟังก์ชันลดของคุณเอง (ซึ่งมักจะช่วยให้เข้าใจฟังก์ชันเช่นนี้ฉันพบ):
test_arr = [];
test_arr.my_reducer = function(reduceFunc, initialMemo) {
const initialMemoIsIndexZero = arguments.length < 2;
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
memo = reduceFunc(memo, this[i]);
}
return memo;
}
การใช้งานจริงช่วยให้สามารถเข้าถึงสิ่งต่างๆเช่นดัชนีได้ แต่ฉันหวังว่านี่จะช่วยให้คุณได้รับความรู้สึกที่ไม่ซับซ้อนสำหรับส่วนสำคัญของมัน