ปลดล็อกพลังของการเขียนโปรแกรมเชิงฟังก์ชันด้วย JavaScript arrays เรียนรู้วิธีการแปลง กรอง และลดข้อมูลของคุณอย่างมีประสิทธิภาพโดยใช้เมธอดในตัว
การเรียนรู้การเขียนโปรแกรมเชิงฟังก์ชันด้วย JavaScript Arrays
ในภูมิทัศน์ของการพัฒนาเว็บที่เปลี่ยนแปลงตลอดเวลา JavaScript ยังคงเป็นรากฐานสำคัญ แม้ว่ากระบวนทัศน์การเขียนโปรแกรมเชิงวัตถุและแบบคำสั่งจะยังคงโดดเด่นมานานแล้ว แต่การเขียนโปรแกรมเชิงฟังก์ชัน (FP) กำลังได้รับความสนใจอย่างมาก FP เน้นย้ำถึงความไม่เปลี่ยนรูป ฟังก์ชันบริสุทธิ์ และโค้ดแบบประกาศ ซึ่งนำไปสู่แอปพลิเคชันที่มีความแข็งแกร่ง สามารถบำรุงรักษาได้ และคาดการณ์ได้มากขึ้น วิธีที่มีประสิทธิภาพที่สุดวิธีหนึ่งในการนำการเขียนโปรแกรมเชิงฟังก์ชันมาใช้ใน JavaScript คือการใช้ประโยชน์จากวิธีการอาร์เรย์ดั้งเดิม
คู่มือที่ครอบคลุมนี้จะเจาะลึกถึงวิธีที่คุณสามารถใช้ประโยชน์จากพลังของหลักการเขียนโปรแกรมเชิงฟังก์ชันโดยใช้อาร์เรย์ JavaScript เราจะสำรวจแนวคิดหลักและสาธิตวิธีนำไปใช้โดยใช้วิธีการต่างๆ เช่น map
, filter
และ reduce
ซึ่งจะเปลี่ยนวิธีที่คุณจัดการการจัดการข้อมูล
การเขียนโปรแกรมเชิงฟังก์ชันคืออะไร
ก่อนที่จะเจาะลึกถึงอาร์เรย์ JavaScript ขอให้เรากำหนดการเขียนโปรแกรมเชิงฟังก์ชันโดยสังเขป ที่แก่นแท้ของ FP คือกระบวนทัศน์การเขียนโปรแกรมที่ถือว่าการคำนวณเป็นการประเมินฟังก์ชันทางคณิตศาสตร์และหลีกเลี่ยงการเปลี่ยนแปลงสถานะและข้อมูลที่เปลี่ยนแปลงได้ หลักการสำคัญ ได้แก่:
- ฟังก์ชันบริสุทธิ์: ฟังก์ชันบริสุทธิ์จะให้ผลลัพธ์เหมือนกันเสมอสำหรับอินพุตเดียวกันและไม่มีผลข้างเคียง (ไม่มีการปรับเปลี่ยนสถานะภายนอก)
- ความไม่เปลี่ยนรูป: ข้อมูลเมื่อสร้างแล้วไม่สามารถเปลี่ยนแปลงได้ แทนที่จะแก้ไขข้อมูลที่มีอยู่ ข้อมูลใหม่จะถูกสร้างขึ้นพร้อมกับการเปลี่ยนแปลงที่ต้องการ
- ฟังก์ชันระดับแรก: ฟังก์ชันสามารถได้รับการปฏิบัติเหมือนตัวแปรอื่นใด พวกเขาสามารถถูกกำหนดให้กับตัวแปร ส่งผ่านเป็นอาร์กิวเมนต์ไปยังฟังก์ชันอื่น และส่งคืนจากฟังก์ชัน
- แบบประกาศเทียบกับแบบสั่ง: การเขียนโปรแกรมเชิงฟังก์ชันจะเอนเอียงไปในรูปแบบแบบประกาศ ซึ่งคุณจะอธิบายว่าคุณต้องการบรรลุ *อะไร* มากกว่ารูปแบบแบบสั่ง ซึ่งมีรายละเอียดว่า *จะ* บรรลุเป้าหมายอย่างไรทีละขั้นตอน
การนำหลักการเหล่านี้ไปใช้อาจนำไปสู่โค้ดที่เข้าใจ ทดสอบ และแก้ไขข้อบกพร่องได้ง่ายขึ้น โดยเฉพาะอย่างยิ่งในแอปพลิเคชันที่ซับซ้อน วิธีการอาร์เรย์ของ JavaScript เหมาะอย่างยิ่งสำหรับการใช้งานแนวคิดเหล่านี้
พลังของวิธีการ Array ของ JavaScript
JavaScript arrays มาพร้อมกับชุดวิธีการในตัวที่หลากหลาย ซึ่งช่วยให้สามารถจัดการข้อมูลที่ซับซ้อนได้โดยไม่ต้องใช้ลูปแบบดั้งเดิม (เช่น for
หรือ while
) วิธีการเหล่านี้มักจะส่งคืนอาร์เรย์ใหม่ ส่งเสริมความไม่เปลี่ยนรูป และยอมรับฟังก์ชันเรียกกลับ ทำให้สามารถเข้าถึงได้ในเชิงฟังก์ชัน
มาสำรวจวิธีการอาร์เรย์พื้นฐานที่สุดกัน:
1. Array.prototype.map()
เมธอด map()
จะสร้างอาร์เรย์ใหม่ที่เต็มไปด้วยผลลัพธ์ของการเรียกใช้ฟังก์ชันที่ให้ไว้ในทุกองค์ประกอบในอาร์เรย์ที่เรียก เหมาะอย่างยิ่งสำหรับการแปลงแต่ละองค์ประกอบของอาร์เรย์ให้เป็นสิ่งใหม่
ไวยากรณ์:
array.map(callback(currentValue[, index[, array]])[, thisArg])
callback
: ฟังก์ชันที่จะดำเนินการสำหรับแต่ละองค์ประกอบcurrentValue
: องค์ประกอบปัจจุบันที่กำลังประมวลผลในอาร์เรย์index
(ไม่บังคับ): ดัชนีขององค์ประกอบปัจจุบันที่กำลังประมวลผลarray
(ไม่บังคับ): อาร์เรย์ที่เรียกว่าmap
thisArg
(ไม่บังคับ): ค่าที่จะใช้เป็นthis
เมื่อดำเนินการcallback
คุณสมบัติหลัก:
- ส่งคืนอาร์เรย์ ใหม่
- อาร์เรย์ดั้งเดิมยังคงไม่เปลี่ยนแปลง (ความไม่เปลี่ยนรูป)
- อาร์เรย์ใหม่จะมีขนาดเท่ากับอาร์เรย์ดั้งเดิม
- ฟังก์ชันเรียกกลับควรส่งคืนค่าที่แปลงแล้วสำหรับแต่ละองค์ประกอบ
ตัวอย่าง: การเพิ่มจำนวนแต่ละจำนวนเป็นสองเท่า
ลองนึกภาพว่าคุณมีอาร์เรย์ของตัวเลขและคุณต้องการสร้างอาร์เรย์ใหม่ที่แต่ละตัวเลขเพิ่มขึ้นเป็นสองเท่า
const numbers = [1, 2, 3, 4, 5];
// ใช้ map สำหรับการแปลง
const doubledNumbers = numbers.map(number => number * 2);
console.log(numbers); // เอาต์พุต: [1, 2, 3, 4, 5] (อาร์เรย์ต้นฉบับยังคงไม่เปลี่ยนแปลง)
console.log(doubledNumbers); // เอาต์พุต: [2, 4, 6, 8, 10]
ตัวอย่าง: การดึงคุณสมบัติจากวัตถุ
กรณีการใช้งานทั่วไปคือการดึงคุณสมบัติเฉพาะจากอาร์เรย์ของวัตถุ สมมติว่าเรามีรายการผู้ใช้และต้องการรับเพียงชื่อของพวกเขา
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userNames = users.map(user => user.name);
console.log(userNames); // เอาต์พุต: ['Alice', 'Bob', 'Charlie']
2. Array.prototype.filter()
เมธอด filter()
สร้างอาร์เรย์ใหม่ที่มีองค์ประกอบทั้งหมดที่ผ่านการทดสอบที่ดำเนินการโดยฟังก์ชันที่ให้ไว้ ใช้เพื่อเลือกองค์ประกอบตามเงื่อนไข
ไวยากรณ์:
array.filter(callback(element[, index[, array]])[, thisArg])
callback
: ฟังก์ชันที่จะดำเนินการสำหรับแต่ละองค์ประกอบ ควรส่งคืนtrue
เพื่อเก็บองค์ประกอบไว้ หรือfalse
เพื่อละทิ้งองค์ประกอบนั้นelement
: องค์ประกอบปัจจุบันที่กำลังประมวลผลในอาร์เรย์index
(ไม่บังคับ): ดัชนีขององค์ประกอบปัจจุบันarray
(ไม่บังคับ): อาร์เรย์ที่เรียกว่าfilter
thisArg
(ไม่บังคับ): ค่าที่จะใช้เป็นthis
เมื่อดำเนินการcallback
คุณสมบัติหลัก:
- ส่งคืนอาร์เรย์ ใหม่
- อาร์เรย์ดั้งเดิมยังคงไม่เปลี่ยนแปลง (ความไม่เปลี่ยนรูป)
- อาร์เรย์ใหม่อาจมีองค์ประกอบน้อยกว่าอาร์เรย์ดั้งเดิม
- ฟังก์ชันเรียกกลับต้องส่งคืนค่าบูลีน
ตัวอย่าง: การกรองตัวเลขคู่
มากรองอาร์เรย์ตัวเลขเพื่อเก็บเฉพาะตัวเลขคู่
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// ใช้ filter เพื่อเลือกตัวเลขคู่
const evenNumbers = numbers.filter(number => number % 2 === 0);
console.log(numbers); // เอาต์พุต: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(evenNumbers); // เอาต์พุต: [2, 4, 6, 8, 10]
ตัวอย่าง: การกรองผู้ใช้ที่ใช้งานอยู่
จากอาร์เรย์ผู้ใช้ของเรา มากรองหาผู้ใช้ที่ถูกระบุว่าใช้งานอยู่
const users = [
{ id: 1, name: 'Alice', isActive: true },
{ id: 2, name: 'Bob', isActive: false },
{ id: 3, name: 'Charlie', isActive: true },
{ id: 4, name: 'David', isActive: false }
];
const activeUsers = users.filter(user => user.isActive);
console.log(activeUsers);
/* เอาต์พุต:
[
{ id: 1, name: 'Alice', isActive: true },
{ id: 3, name: 'Charlie', isActive: true }
]
*/
3. Array.prototype.reduce()
เมธอด reduce()
จะดำเนินการฟังก์ชันเรียกกลับ “ตัวลด” ที่ผู้ใช้จัดหาให้ในแต่ละองค์ประกอบของอาร์เรย์ตามลำดับ โดยส่งผ่านค่าที่ส่งคืนจากการคำนวณบนองค์ประกอบก่อนหน้า ผลลัพธ์สุดท้ายของการเรียกใช้ตัวลดทั่วทั้งองค์ประกอบทั้งหมดของอาร์เรย์คือค่าเดียว
นี่เป็นวิธีการอาร์เรย์ที่ใช้งานได้หลากหลายที่สุดและเป็นรากฐานสำคัญของรูปแบบการเขียนโปรแกรมเชิงฟังก์ชันจำนวนมาก ทำให้คุณสามารถ “ลด” อาร์เรย์เป็นค่าเดียว (เช่น ผลรวม ผลิตภัณฑ์ จำนวน หรือแม้แต่วัตถุหรืออาร์เรย์ใหม่)
ไวยากรณ์:
array.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback
: ฟังก์ชันที่จะดำเนินการสำหรับแต่ละองค์ประกอบaccumulator
: ค่าที่เกิดจากการเรียกใช้ฟังก์ชันเรียกกลับก่อนหน้า ในการเรียกใช้ครั้งแรกจะเป็นinitialValue
หากระบุไว้ มิฉะนั้นจะเป็นองค์ประกอบแรกของอาร์เรย์currentValue
: องค์ประกอบปัจจุบันที่กำลังประมวลผลindex
(ไม่บังคับ): ดัชนีขององค์ประกอบปัจจุบันarray
(ไม่บังคับ): อาร์เรย์ที่เรียกว่าreduce
initialValue
(ไม่บังคับ): ค่าที่จะใช้เป็นอาร์กิวเมนต์แรกสำหรับการเรียกใช้ครั้งแรกของcallback
หากไม่มีการจัดหาinitialValue
องค์ประกอบแรกในอาร์เรย์จะถูกใช้เป็นค่าaccumulator
เริ่มต้น และการวนซ้ำจะเริ่มต้นจากองค์ประกอบที่สอง
คุณสมบัติหลัก:
- ส่งคืน ค่าเดียว (ซึ่งอาจเป็นอาร์เรย์หรือวัตถุก็ได้)
- อาร์เรย์ดั้งเดิมยังคงไม่เปลี่ยนแปลง (ความไม่เปลี่ยนรูป)
initialValue
มีความสำคัญอย่างยิ่งเพื่อความชัดเจนและหลีกเลี่ยงข้อผิดพลาด โดยเฉพาะอย่างยิ่งกับอาร์เรย์ที่ว่างเปล่าหรือเมื่อชนิดตัวสะสมแตกต่างจากชนิดองค์ประกอบอาร์เรย์
ตัวอย่าง: การรวมตัวเลข
มาบวกตัวเลขทั้งหมดในอาร์เรย์ของเรากัน
const numbers = [1, 2, 3, 4, 5];
// ใช้ reduce เพื่อรวมตัวเลข
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // 0 คือ initialValue
console.log(sum); // เอาต์พุต: 15
คำอธิบาย:
- เรียก 1:
accumulator
คือ 0,currentValue
คือ 1 ส่งคืน 0 + 1 = 1 - เรียก 2:
accumulator
คือ 1,currentValue
คือ 2 ส่งคืน 1 + 2 = 3 - เรียก 3:
accumulator
คือ 3,currentValue
คือ 3 ส่งคืน 3 + 3 = 6 - และอื่น ๆ จนกว่าจะคำนวณผลรวมสุดท้าย
ตัวอย่าง: การจัดกลุ่มวัตถุตามคุณสมบัติ
เราสามารถใช้ reduce
เพื่อแปลงอาร์เรย์ของวัตถุเป็นวัตถุที่ค่าถูกจัดกลุ่มตามคุณสมบัติเฉพาะ มาจัดกลุ่มผู้ใช้ของเราตามสถานะ `isActive` ของพวกเขา
const users = [
{ id: 1, name: 'Alice', isActive: true },
{ id: 2, name: 'Bob', isActive: false },
{ id: 3, name: 'Charlie', isActive: true },
{ id: 4, name: 'David', isActive: false }
];
const groupedUsers = users.reduce((acc, user) => {
const status = user.isActive ? 'active' : 'inactive';
if (!acc[status]) {
acc[status] = [];
}
acc[status].push(user);
return acc;
}, {}); // วัตถุว่างเปล่า {} คือ initialValue
console.log(groupedUsers);
/* เอาต์พุต:
{
active: [
{ id: 1, name: 'Alice', isActive: true },
{ id: 3, name: 'Charlie', isActive: true }
],
inactive: [
{ id: 2, name: 'Bob', isActive: false },
{ id: 4, name: 'David', isActive: false }
]
}
*/
ตัวอย่าง: การนับจำนวนครั้งที่ปรากฏ
มานับความถี่ของผลไม้แต่ละชนิดในรายการกัน
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const fruitCounts = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(fruitCounts); // เอาต์พุต: { apple: 3, banana: 2, orange: 1 }
4. Array.prototype.forEach()
ในขณะที่ forEach()
ไม่ได้ส่งคืนอาร์เรย์ใหม่ และมักถูกพิจารณาว่าเป็นแบบสั่งมากกว่า เนื่องจากจุดประสงค์หลักคือการดำเนินการฟังก์ชันสำหรับแต่ละองค์ประกอบอาร์เรย์ มันยังคงเป็นวิธีการพื้นฐานที่มีบทบาทในรูปแบบเชิงฟังก์ชัน โดยเฉพาะอย่างยิ่งเมื่อจำเป็นต้องมีผลข้างเคียง หรือเมื่อมีการทำซ้ำโดยไม่จำเป็นต้องมีผลลัพธ์ที่เปลี่ยนแปลง
ไวยากรณ์:
array.forEach(callback(element[, index[, array]])[, thisArg])
คุณสมบัติหลัก:
- ส่งคืน
undefined
- ดำเนินการฟังก์ชันที่ให้ไว้หนึ่งครั้งสำหรับแต่ละองค์ประกอบอาร์เรย์
- มักใช้สำหรับผลข้างเคียง เช่น การบันทึกไปยังคอนโซลหรือการอัปเดตองค์ประกอบ DOM
ตัวอย่าง: การบันทึกแต่ละองค์ประกอบ
const messages = ['Hello', 'Functional', 'World'];
messages.forEach(message => console.log(message));
// เอาต์พุต:
// Hello
// Functional
// World
หมายเหตุ: สำหรับการแปลงและการกรอง map
และ filter
เป็นที่ต้องการเนื่องจากความไม่เปลี่ยนรูปและลักษณะแบบประกาศ ใช forEach
เมื่อคุณต้องการดำเนินการเฉพาะสำหรับแต่ละรายการโดยไม่ต้องรวบรวมผลลัพธ์ลงในโครงสร้างใหม่
5. Array.prototype.find()
และ Array.prototype.findIndex()
วิธีการเหล่านี้มีประโยชน์สำหรับการระบุตำแหน่งองค์ประกอบเฉพาะในอาร์เรย์
find()
: ส่งคืน ค่า ขององค์ประกอบแรกในอาร์เรย์ที่ให้ไว้ซึ่งตรงตามฟังก์ชันการทดสอบที่ให้ไว้ หากไม่มีค่าใดที่ตรงตามฟังก์ชันการทดสอบundefined
จะถูกส่งคืนfindIndex()
: ส่งคืน ดัชนี ขององค์ประกอบแรกในอาร์เรย์ที่ให้ไว้ซึ่งตรงตามฟังก์ชันการทดสอบที่ให้ไว้ มิฉะนั้นจะส่งคืน -1 ซึ่งบ่งชี้ว่าไม่มีองค์ประกอบใดผ่านการทดสอบ
ตัวอย่าง: การค้นหาผู้ใช้
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const bob = users.find(user => user.name === 'Bob');
const bobIndex = users.findIndex(user => user.name === 'Bob');
const nonExistentUser = users.find(user => user.name === 'David');
const nonExistentIndex = users.findIndex(user => user.name === 'David');
console.log(bob); // เอาต์พุต: { id: 2, name: 'Bob' }
console.log(bobIndex); // เอาต์พุต: 1
console.log(nonExistentUser); // เอาต์พุต: undefined
console.log(nonExistentIndex); // เอาต์พุต: -1
6. Array.prototype.some()
และ Array.prototype.every()
วิธีการเหล่านี้ทดสอบว่าองค์ประกอบทั้งหมดในอาร์เรย์ผ่านการทดสอบที่ดำเนินการโดยฟังก์ชันที่ให้ไว้
some()
: ทดสอบว่ามีองค์ประกอบอย่างน้อยหนึ่งองค์ประกอบในอาร์เรย์ที่ผ่านการทดสอบที่ดำเนินการโดยฟังก์ชันที่ให้ไว้หรือไม่ ส่งคืนค่าบูลีนevery()
: ทดสอบว่าองค์ประกอบทั้งหมดในอาร์เรย์ผ่านการทดสอบที่ดำเนินการโดยฟังก์ชันที่ให้ไว้หรือไม่ ส่งคืนค่าบูลีน
ตัวอย่าง: การตรวจสอบสถานะผู้ใช้
const users = [
{ id: 1, name: 'Alice', isActive: true },
{ id: 2, name: 'Bob', isActive: false },
{ id: 3, name: 'Charlie', isActive: true }
];
const hasInactiveUser = users.some(user => !user.isActive);
const allAreActive = users.every(user => user.isActive);
console.log(hasInactiveUser); // เอาต์พุต: true (เพราะ Bob ไม่ได้ใช้งาน)
console.log(allAreActive); // เอาต์พุต: false (เพราะ Bob ไม่ได้ใช้งาน)
const allUsersActive = users.filter(user => user.isActive).length === users.length;
console.log(allUsersActive); // เอาต์พุต: false
// ทางเลือกอื่นโดยใช้ every โดยตรง
const allUsersActiveDirect = users.every(user => user.isActive);
console.log(allUsersActiveDirect); // เอาต์พุต: false
การเชื่อมโยงวิธีการ Array สำหรับการดำเนินการที่ซับซ้อน
พลังที่แท้จริงของการเขียนโปรแกรมเชิงฟังก์ชันด้วย JavaScript arrays จะเปล่งประกายเมื่อคุณเชื่อมโยงวิธีการเหล่านี้เข้าด้วยกัน เนื่องจากวิธีการส่วนใหญ่เหล่านี้ส่งคืนอาร์เรย์ใหม่ (ยกเว้น forEach
) คุณจึงสามารถส่งออกผลลัพธ์ของวิธีหนึ่งไปยังอินพุตของอีกวิธีหนึ่งได้อย่างราบรื่น สร้างไปป์ไลน์ข้อมูลที่สง่างามและอ่านง่าย
ตัวอย่าง: การค้นหาชื่อผู้ใช้ที่ใช้งานอยู่และเพิ่ม ID เป็นสองเท่า
มาค้นหาผู้ใช้ที่ใช้งานอยู่ทั้งหมด ดึงชื่อของพวกเขา จากนั้นสร้างอาร์เรย์ใหม่ที่แต่ละชื่อนำหน้าด้วยตัวเลขที่แสดงถึงดัชนีในรายการ *ที่กรองแล้ว* และ ID ของพวกเขาเพิ่มขึ้นเป็นสองเท่า
const users = [
{ id: 1, name: 'Alice', isActive: true },
{ id: 2, name: 'Bob', isActive: false },
{ id: 3, name: 'Charlie', isActive: true },
{ id: 4, name: 'David', isActive: true },
{ id: 5, name: 'Eve', isActive: false }
];
const processedActiveUsers = users
.filter(user => user.isActive) // รับเฉพาะผู้ใช้ที่ใช้งานอยู่
.map((user, index) => ({ // แปลงผู้ใช้ที่ใช้งานอยู่แต่ละราย
name: `${index + 1}. ${user.name}`,
doubledId: user.id * 2
}));
console.log(processedActiveUsers);
/* เอาต์พุต:
[
{ name: '1. Alice', doubledId: 2 },
{ name: '2. Charlie', doubledId: 6 },
{ name: '3. David', doubledId: 8 }
]
*/
แนวทางแบบลูกโซ่นี้เป็นแบบประกาศ: เราระบุขั้นตอน (กรอง จากนั้นทำแผนที่) โดยไม่มีการจัดการลูปที่ชัดเจน นอกจากนี้ยังไม่สามารถเปลี่ยนแปลงได้ เนื่องจากแต่ละขั้นตอนจะสร้างอาร์เรย์หรือวัตถุใหม่ โดยปล่อยให้อาร์เรย์ users
ดั้งเดิมไม่เปลี่ยนแปลง
ความไม่เปลี่ยนรูปในการปฏิบัติ
การเขียนโปรแกรมเชิงฟังก์ชันอาศัยความไม่เปลี่ยนรูปเป็นอย่างมาก ซึ่งหมายความว่าแทนที่จะปรับเปลี่ยนโครงสร้างข้อมูลที่มีอยู่ คุณจะสร้างโครงสร้างข้อมูลใหม่พร้อมกับการเปลี่ยนแปลงที่ต้องการ วิธีการอาร์เรย์ของ JavaScript เช่น map
, filter
และ slice
สนับสนุนสิ่งนี้โดยส่งคืนอาร์เรย์ใหม่
เหตุใดความไม่เปลี่ยนรูปจึงมีความสำคัญ
- ความสามารถในการคาดการณ์: โค้ดจะกลายเป็นเรื่องง่ายต่อการพิจารณาเหตุผล เนื่องจากคุณไม่ต้องติดตามการเปลี่ยนแปลงในสถานะที่ใช้ร่วมกันที่เปลี่ยนแปลงได้
- การแก้ไขข้อบกพร่อง: เมื่อเกิดข้อบกพร่อง จะง่ายต่อการระบุแหล่งที่มาของปัญหาเมื่อข้อมูลไม่ถูกปรับเปลี่ยนโดยไม่คาดคิด
- ประสิทธิภาพ: ในบริบทบางอย่าง (เช่น ด้วยไลบรารีการจัดการสถานะ เช่น Redux หรือใน React) ความไม่เปลี่ยนรูปช่วยให้ตรวจจับการเปลี่ยนแปลงได้อย่างมีประสิทธิภาพ
- พร้อมกัน: โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปนั้นปลอดภัยต่อเธรดโดยเนื้อแท้ ซึ่งทำให้การเขียนโปรแกรมพร้อมกันง่ายขึ้น
เมื่อคุณต้องดำเนินการที่จะเปลี่ยนแปลงอาร์เรย์แบบดั้งเดิม (เช่น การเพิ่มหรือลบองค์ประกอบ) คุณสามารถบรรลุความไม่เปลี่ยนรูปโดยใช้วิธีการต่างๆ เช่น slice
ไวยากรณ์การแพร่กระจาย (...
) หรือโดยการรวมวิธีการเชิงฟังก์ชันอื่นๆ
ตัวอย่าง: การเพิ่มองค์ประกอบอย่างไม่เปลี่ยนรูป
const originalArray = [1, 2, 3];
// วิธีการสั่ง (ปรับเปลี่ยน originalArray)
// originalArray.push(4);
// วิธีการเชิงฟังก์ชันโดยใช้ไวยากรณ์การแพร่กระจาย
const newArrayWithPush = [...originalArray, 4];
console.log(originalArray); // เอาต์พุต: [1, 2, 3]
console.log(newArrayWithPush); // เอาต์พุต: [1, 2, 3, 4]
// วิธีการเชิงฟังก์ชันโดยใช้ slice และการเชื่อมต่อ (น้อยกว่าทั่วไปในตอนนี้)
const newArrayWithSlice = originalArray.slice(0, originalArray.length).concat(4);
console.log(newArrayWithSlice); // เอาต์พุต: [1, 2, 3, 4]
ตัวอย่าง: การลบองค์ประกอบอย่างไม่เปลี่ยนรูป
const originalArray = [1, 2, 3, 4, 5];
// ลบองค์ประกอบที่ดัชนี 2 (ค่า 3)
// วิธีการเชิงฟังก์ชันโดยใช้ slice และไวยากรณ์การแพร่กระจาย
const newArrayAfterSplice = [
...originalArray.slice(0, 2),
...originalArray.slice(3)
];
console.log(originalArray); // เอาต์พุต: [1, 2, 3, 4, 5]
console.log(newArrayAfterSplice); // เอาต์พุต: [1, 2, 4, 5]
// ใช้ filter เพื่อลบค่าเฉพาะ
const newValueToRemove = 3;
const arrayWithoutValue = originalArray.filter(item => item !== newValueToRemove);
console.log(arrayWithoutValue); // เอาต์พุต: [1, 2, 4, 5]
แนวทางปฏิบัติที่ดีที่สุดและเทคนิคขั้นสูง
เมื่อคุณคุ้นเคยกับวิธีการอาร์เรย์เชิงฟังก์ชันมากขึ้น ให้พิจารณาแนวทางปฏิบัติต่อไปนี้:
- อ่านง่ายเป็นอันดับแรก: ในขณะที่การเชื่อมโยงมีประสิทธิภาพ ลูกโซ่ที่ยาวเกินไปอาจอ่านยาก พิจารณาแบ่งการดำเนินการที่ซับซ้อนออกเป็นฟังก์ชันที่มีชื่อเล็กกว่า หรือใช้ตัวแปรกลาง
- ทำความเข้าใจความยืดหยุ่นของ `reduce`: โปรดจำไว้ว่า
reduce
สามารถสร้างอาร์เรย์หรือวัตถุ ไม่ใช่แค่ค่าเดียว ซึ่งทำให้ใช้งานได้หลากหลายอย่างเหลือเชื่อสำหรับการแปลงที่ซับซ้อน - หลีกเลี่ยงผลข้างเคียงในฟังก์ชันเรียกกลับ: พยายามรักษา
map
,filter
และreduce
ฟังก์ชันเรียกกลับของคุณให้บริสุทธิ์ หากคุณต้องการดำเนินการที่มีผลข้างเคียงforEach
มักจะเป็นตัวเลือกที่เหมาะสมกว่า - ใช้ฟังก์ชัน Arrow: ฟังก์ชัน Arrow (
=>
) ให้ไวยากรณ์ที่กระชับสำหรับฟังก์ชันเรียกกลับและจัดการการผูกthis
แตกต่างกัน ซึ่งมักจะทำให้เหมาะสำหรับวิธีการอาร์เรย์เชิงฟังก์ชัน - พิจารณาไลบรารี: สำหรับรูปแบบการเขียนโปรแกรมเชิงฟังก์ชันขั้นสูง หรือหากคุณทำงานอย่างกว้างขวางกับความไม่เปลี่ยนรูป ไลบรารีเช่น Lodash/fp, Ramda หรือ Immutable.js อาจเป็นประโยชน์ แม้ว่าจะไม่จำเป็นอย่างเคร่งครัดในการเริ่มต้นใช้งานการดำเนินการอาร์เรย์เชิงฟังก์ชันใน JavaScript สมัยใหม่
ตัวอย่าง: แนวทางเชิงฟังก์ชันในการรวมข้อมูล
ลองนึกภาพว่าคุณมีข้อมูลยอดขายจากภูมิภาคต่างๆ และต้องการคำนวณยอดขายรวมสำหรับแต่ละภูมิภาค จากนั้นค้นหาภูมิภาคที่มียอดขายสูงสุด
const salesData = [
{ region: 'North', amount: 100 },
{ region: 'South', amount: 150 },
{ region: 'North', amount: 120 },
{ region: 'East', amount: 200 },
{ region: 'South', amount: 180 },
{ region: 'North', amount: 90 }
];
// 1. คำนวณยอดขายรวมต่อภูมิภาคโดยใช้ reduce
const salesByRegion = salesData.reduce((acc, sale) => {
acc[sale.region] = (acc[sale.region] || 0) + sale.amount;
return acc;
}, {});
// salesByRegion จะเป็น: { North: 310, South: 330, East: 200 }
// 2. แปลงวัตถุที่รวมเป็นอาร์เรย์ของวัตถุเพื่อการประมวลผลเพิ่มเติม
const salesArray = Object.keys(salesByRegion).map(region => ({
region: region,
totalAmount: salesByRegion[region]
}));
// salesArray จะเป็น: [
// { region: 'North', totalAmount: 310 },
// { region: 'South', totalAmount: 330 },
// { region: 'East', totalAmount: 200 }
// ]
// 3. ค้นหาภูมิภาคที่มียอดขายสูงสุดโดยใช้ reduce
const highestSalesRegion = salesArray.reduce((max, current) => {
return current.totalAmount > max.totalAmount ? current : max;
}, { region: '', totalAmount: -Infinity }); // เริ่มต้นด้วยตัวเลขที่น้อยมาก
console.log('Sales by Region:', salesByRegion);
console.log('Sales Array:', salesArray);
console.log('Region with Highest Sales:', highestSalesRegion);
/*
เอาต์พุต:
Sales by Region: { North: 310, South: 330, East: 200 }
Sales Array: [
{ region: 'North', totalAmount: 310 },
{ region: 'South', totalAmount: 330 },
{ region: 'East', totalAmount: 200 }
]
Region with Highest Sales: { region: 'South', totalAmount: 330 }
*/
บทสรุป
การเขียนโปรแกรมเชิงฟังก์ชันด้วย JavaScript arrays ไม่ได้เป็นเพียงทางเลือกด้านสไตล์เท่านั้น มันเป็นวิธีที่มีประสิทธิภาพในการเขียนโค้ดที่สะอาดกว่า คาดการณ์ได้มากขึ้น และแข็งแกร่งยิ่งขึ้น ด้วยการนำวิธีการต่างๆ เช่น map
, filter
และ reduce
มาใช้ คุณสามารถแปลง สอบถาม และรวมข้อมูลของคุณได้อย่างมีประสิทธิภาพในขณะที่ปฏิบัติตามหลักการพื้นฐานของการเขียนโปรแกรมเชิงฟังก์ชัน โดยเฉพาะอย่างยิ่งความไม่เปลี่ยนรูปและฟังก์ชันบริสุทธิ์
เมื่อคุณยังคงเดินทางในการพัฒนา JavaScript การรวมรูปแบบเชิงฟังก์ชันเหล่านี้เข้ากับเวิร์กโฟลว์ประจำวันของคุณจะนำไปสู่แอปพลิเคชันที่สามารถบำรุงรักษาและปรับขนาดได้มากขึ้นอย่างไม่ต้องสงสัย เริ่มต้นด้วยการทดลองกับวิธีการอาร์เรย์เหล่านี้ในโปรเจ็กต์ของคุณ แล้วคุณจะค้นพบคุณค่ามหาศาลของมันในไม่ช้า