Article · programming
JavaScript Functions: สร้างฟังก์ชันให้โค้ดทำงานซ้ำได้
JavaScript
EP 9 ของซีรีส์ JavaScript คืออะไร? เริ่มต้นเขียนโปรแกรม เรียนรู้การสร้างฟังก์ชัน การรับพารามิเตอร์ การส่งค่ากลับ และการนำฟังก์ชันไปประยุกต์ใช้กับข้อมูลจริง
EP 9: Functions หัวใจของการเขียนโปรแกรมที่นำกลับมาใช้ซ้ำได้
จากตอนก่อนที่เราเรียนรู้การจัดเก็บข้อมูลด้วย Array และ Object ตอนนี้จะพาไปรู้จักสิ่งสำคัญอีกอย่างหนึ่งของ JavaScript นั่นคือ **Function หรือฟังก์ชัน** ซึ่งเปรียบเสมือนชุดคำสั่งที่เราสร้างขึ้นเพื่อทำงานเฉพาะอย่าง เมื่อจำเป็นต้องใช้งาน เราสามารถเรียกฟังก์ชันเดิมได้หลายครั้งโดยไม่ต้องเขียนคำสั่งทั้งหมดซ้ำอีก
แนวคิดสำคัญฟังก์ชันช่วยแบ่งโปรแกรมขนาดใหญ่ให้เป็นส่วนย่อยที่เข้าใจง่าย ทดสอบสะดวก และดูแลรักษาได้ง่ายขึ้น โดยแต่ละฟังก์ชันควรมีหน้าที่ชัดเจน
1. ฟังก์ชันคืออะไร?
ลองนึกถึงเครื่องชงกาแฟ เราใส่วัตถุดิบ กดปุ่ม แล้วเครื่องทำงานตามขั้นตอนที่กำหนดไว้ ฟังก์ชันก็มีแนวคิดคล้ายกัน เราสามารถส่งข้อมูลเข้าไป ให้ชุดคำสั่งประมวลผล และรับผลลัพธ์กลับมาได้ ทั้งนี้ ฟังก์ชันบางตัวอาจไม่ต้องรับข้อมูลหรือส่งค่ากลับเลยก็ได้
function sayHello() {
console.log('สวัสดี JavaScript!');
}
sayHello();
sayHello();
ตัวอย่างนี้สร้างฟังก์ชันชื่อ `sayHello` ด้วยคำสั่ง `function` ภายในมีคำสั่งแสดงข้อความผ่าน `console.log()` เมื่อเรียก `sayHello()` แต่ละครั้ง โปรแกรมจะทำงานตามคำสั่งที่อยู่ในฟังก์ชัน ทำให้ข้อความถูกแสดงสองครั้ง
2. ส่วนประกอบของฟังก์ชัน
ฟังก์ชันแบบประกาศด้วย `function` โดยทั่วไปประกอบด้วยชื่อฟังก์ชัน วงเล็บสำหรับระบุพารามิเตอร์ ปีกกาสำหรับบรรจุชุดคำสั่ง และอาจมี `return` เพื่อส่งผลลัพธ์กลับไปยังจุดที่เรียกใช้งาน
function calculateTotal(price, quantity) {
const total = price * quantity;
return total;
}
const result = calculateTotal(120, 3);
console.log(result);
| ส่วนประกอบ | ความหมาย | ตัวอย่าง |
|---|---|---|
| function | คำสั่งประกาศฟังก์ชัน | function calculateTotal |
| ชื่อฟังก์ชัน | ชื่อที่ใช้เรียกชุดคำสั่ง | calculateTotal |
| พารามิเตอร์ | ตัวแปรที่รับข้อมูลเข้ามา | price, quantity |
| ชุดคำสั่ง | คำสั่งที่ฟังก์ชันต้องดำเนินการ | const total = price * quantity |
| return | ส่งค่าผลลัพธ์กลับไปยังผู้เรียก | return total |
3. พารามิเตอร์และอาร์กิวเมนต์ต่างกันอย่างไร?
**พารามิเตอร์ (Parameter)** คือตัวแปรที่กำหนดไว้ในวงเล็บตอนสร้างฟังก์ชัน ส่วน **อาร์กิวเมนต์ (Argument)** คือค่าจริงที่ส่งเข้ามาตอนเรียกฟังก์ชัน ความแตกต่างนี้ช่วยให้เราเข้าใจว่าฟังก์ชันเดียวกันสามารถรับข้อมูลต่างกันและให้ผลลัพธ์ต่างกันได้
function greet(name) {
console.log(`สวัสดี ${name}`);
}
greet('มิน');
greet('นัท');
greet('พลอย');
ในตัวอย่าง `name` เป็นพารามิเตอร์ ส่วน `'มิน'`, `'นัท'` และ `'พลอย'` เป็นอาร์กิวเมนต์ ฟังก์ชัน `greet` จึงนำกลับมาใช้กับชื่อที่แตกต่างกันได้โดยไม่ต้องสร้างฟังก์ชันใหม่สำหรับแต่ละคน
กำหนดค่าเริ่มต้นให้พารามิเตอร์
หากต้องการให้ฟังก์ชันมีค่าที่ใช้เมื่อผู้เรียกไม่ได้ส่งอาร์กิวเมนต์มา เราสามารถกำหนด Default Parameter ได้ ช่วยลดกรณีที่ข้อมูลบางส่วนไม่มีค่า
function greetUser(name = 'ผู้ใช้งาน') {
return `ยินดีต้อนรับ ${name}`;
}
console.log(greetUser('ออม'));
console.log(greetUser());
ผลลัพธ์ที่ควรเข้าใจการเรียก `greetUser('ออม')` ใช้ชื่อออม ส่วน `greetUser()` ใช้ค่าเริ่มต้นว่า ผู้ใช้งาน ค่าเริ่มต้นจะถูกนำมาใช้เมื่ออาร์กิวเมนต์ที่เกี่ยวข้องเป็น `undefined`
4. return สำคัญอย่างไร?
`return` ใช้ส่งค่าจากฟังก์ชันกลับไปยังตำแหน่งที่เรียกใช้งาน และเมื่อโปรแกรมทำงานถึง `return` ฟังก์ชันนั้นจะจบการทำงานทันที หากฟังก์ชันไม่มีคำสั่ง `return` ที่ส่งค่าออกมา ผลลัพธ์ที่ได้รับจะเป็น `undefined`
function add(a, b) {
return a + b;
}
function addAndPrint(a, b) {
console.log(a + b);
}
const sum = add(10, 5);
const printed = addAndPrint(10, 5);
console.log('ค่าที่ส่งกลับ:', sum);
console.log('ค่าที่ได้รับ:', printed);
ฟังก์ชัน `add` ส่งผลรวมกลับมา จึงนำค่าไปเก็บในตัวแปร `sum` หรือคำนวณต่อได้ ส่วน `addAndPrint` แสดงผลด้วย `console.log()` เท่านั้นและไม่มี `return` จึงไม่ได้ส่งผลรวมกลับมาให้ตัวแปร `printed`
อย่าสับสนระหว่างการแสดงผลกับการส่งค่ากลับ`console.log()` มีหน้าที่แสดงข้อมูล ส่วน `return` มีหน้าที่ส่งค่ากลับจากฟังก์ชัน ทั้งสองอย่างอาจใช้ร่วมกันได้ แต่ไม่ใช่สิ่งเดียวกัน
5. Function Declaration และ Function Expression
JavaScript มีหลายวิธีในการสร้างฟังก์ชัน รูปแบบแรกคือ Function Declaration ซึ่งประกาศด้วย `function` และมีชื่อฟังก์ชันโดยตรง ส่วน Function Expression คือการนำฟังก์ชันไปกำหนดให้ตัวแปร ทั้งสองรูปแบบใช้สร้างพฤติกรรมที่ต้องการได้ แต่มีรายละเอียดด้านการประกาศและการเรียกใช้แตกต่างกัน
function square(number) {
return number * number;
}
const cube = function (number) {
return number * number * number;
};
console.log(square(4));
console.log(cube(3));
ตัวอย่างแรกประกาศฟังก์ชันชื่อ `square` โดยตรง ส่วนตัวอย่างที่สองสร้างฟังก์ชันแล้วเก็บไว้ในตัวแปร `cube` เมื่อใช้ Function Expression ที่กำหนดให้ตัวแปรด้วย `const` ต้องประกาศตัวแปรก่อนเรียกใช้งาน
6. Arrow Function รูปแบบกระชับที่นิยมใช้
Arrow Function ใช้เครื่องหมาย `=>` เพื่อสร้างฟังก์ชันในรูปแบบที่กระชับ มักพบในการเขียน JavaScript สมัยใหม่ โดยเฉพาะฟังก์ชันที่ส่งให้เมธอดของ Array เช่น `map()` และ `filter()`
const double = number => number * 2;
const add = (a, b) => a + b;
const isAdult = age => age >= 18;
console.log(double(6));
console.log(add(7, 8));
console.log(isAdult(20));
เมื่อ Arrow Function มีนิพจน์เดียวและไม่ใช้ปีกกา สามารถละคำว่า `return` ได้ เพราะจะส่งค่านิพจน์นั้นกลับโดยอัตโนมัติ หากใช้ปีกกาเพื่อเขียนชุดคำสั่งหลายบรรทัด ต้องเขียน `return` เองเมื่อจำเป็นต้องส่งค่ากลับ
const calculateDiscount = (price, percent) => {
const discount = price * percent / 100;
const finalPrice = price - discount;
return finalPrice;
};
console.log(calculateDiscount(1000, 15));
7. นำฟังก์ชันไปทำงานร่วมกับ Array
ฟังก์ชันมีประโยชน์มากเมื่อใช้กับข้อมูลหลายรายการ เราสามารถนำ Arrow Function ไปใช้กับ `map()` เพื่อแปลงข้อมูลแต่ละรายการ หรือใช้ `filter()` เพื่อเลือกเฉพาะรายการที่ผ่านเงื่อนไข ซึ่งเป็นการต่อยอดความรู้จากตอน Arrays ได้อย่างเป็นธรรมชาติ
const prices = [100, 250, 80, 400, 150];
const pricesWithVat = prices.map(price => price * 1.07);
const expensivePrices = prices.filter(price => price >= 200);
const totalPrice = prices.reduce((sum, price) => sum + price, 0);
console.log('ราคาพร้อม VAT:', pricesWithVat);
console.log('ราคาตั้งแต่ 200 ขึ้นไป:', expensivePrices);
console.log('ราคารวม:', totalPrice);
| เมธอด | หน้าที่ | ผลลัพธ์ที่ได้ |
|---|---|---|
| map() | แปลงแต่ละรายการ | Array ใหม่ที่มีค่าจากการแปลง |
| filter() | เลือกข้อมูลตามเงื่อนไข | Array ใหม่ที่มีเฉพาะรายการที่ผ่านเงื่อนไข |
| reduce() | รวมข้อมูลให้เป็นผลลัพธ์เดียว | ค่าเดียว เช่น ผลรวม |
8. ขอบเขตตัวแปรกับฟังก์ชัน
ตัวแปรที่ประกาศภายในฟังก์ชันด้วย `let` หรือ `const` จะอยู่ในขอบเขตของบล็อกที่ประกาศ ตัวแปรเหล่านี้ไม่สามารถเรียกใช้จากภายนอกฟังก์ชันได้โดยตรง การแยกขอบเขตช่วยป้องกันไม่ให้ข้อมูลภายในไปชนกับตัวแปรอื่นโดยไม่ตั้งใจ
const message = 'ข้อความจากภายนอก';
function showMessage() {
const localMessage = 'ข้อความภายในฟังก์ชัน';
console.log(message);
console.log(localMessage);
}
showMessage();
console.log(message);
// console.log(localMessage); จะเกิด ReferenceError
ตัวแปร `message` ถูกประกาศภายนอกฟังก์ชัน จึงเข้าถึงได้ในตัวอย่างนี้ทั้งจากภายนอกและภายในฟังก์ชัน ส่วน `localMessage` ถูกประกาศภายใน `showMessage` จึงไม่สามารถเข้าถึงจากภายนอกได้
9. ตัวอย่างประยุกต์: คำนวณยอดสั่งซื้อ
สมมติว่าเรากำลังพัฒนาระบบร้านค้าออนไลน์ มีรายการสินค้าเก็บอยู่ใน Array ของ Object เราสามารถสร้างฟังก์ชันสำหรับคำนวณราคารวม ส่วนลด และยอดชำระสุทธิ แทนการเขียนสูตรเดิมซ้ำในหลายหน้าของระบบ
const cart = [
{ name: 'สมุด', price: 35, quantity: 2 },
{ name: 'ปากกา', price: 12, quantity: 5 },
{ name: 'กระเป๋า', price: 450, quantity: 1 }
];
function calculateSubtotal(items) {
return items.reduce((sum, item) => {
return sum + item.price * item.quantity;
}, 0);
}
function calculateDiscount(subtotal, percent = 0) {
return subtotal * percent / 100;
}
function calculateOrder(items, discountPercent = 0) {
const subtotal = calculateSubtotal(items);
const discount = calculateDiscount(subtotal, discountPercent);
return {
subtotal,
discount,
total: subtotal - discount
};
}
const order = calculateOrder(cart, 10);
console.log('ยอดก่อนหักส่วนลด:', order.subtotal);
console.log('ส่วนลด:', order.discount);
console.log('ยอดชำระสุทธิ:', order.total);
ตัวอย่างนี้แยกความรับผิดชอบเป็นสามส่วน ได้แก่ การหายอดรวมสินค้า การคำนวณส่วนลด และการรวมผลลัพธ์ของคำสั่งซื้อ แต่ละฟังก์ชันสามารถนำไปทดสอบหรือปรับปรุงแยกกันได้ หากระบบต้องเพิ่มภาษี ค่าจัดส่ง หรือคูปองส่วนลด ก็สามารถต่อยอดได้ง่ายกว่าเขียนทุกอย่างรวมกันในฟังก์ชันเดียว
ข้อควรระวังในระบบเงินจริงตัวอย่างนี้ใช้ตัวเลข JavaScript ทั่วไปเพื่อการเรียนรู้ ระบบการเงินที่ต้องการความแม่นยำควรออกแบบการเก็บจำนวนเงินอย่างเหมาะสม เช่น ใช้หน่วยย่อยจำนวนเต็มหรือไลบรารีคำนวณทศนิยม และตรวจสอบข้อมูลที่รับเข้ามาก่อนคำนวณ
10. แนวทางเขียนฟังก์ชันที่ดี
ฟังก์ชันที่ดีไม่จำเป็นต้องสั้นที่สุด แต่ควรมีหน้าที่ชัดเจน ตั้งชื่อให้สื่อความหมาย รับเฉพาะข้อมูลที่จำเป็น และส่งผลลัพธ์ในรูปแบบที่ผู้เรียกเข้าใจได้ การแยกงานออกเป็นฟังก์ชันย่อยช่วยลดการทำซ้ำและทำให้ตรวจหาข้อผิดพลาดได้ง่ายขึ้น
| แนวทาง | ตัวอย่างที่ควรเลือก | เหตุผล |
|---|---|---|
| ตั้งชื่อให้สื่อความหมาย | calculateTotal() | อ่านแล้วเข้าใจหน้าที่ |
| ทำหน้าที่เดียวให้ชัดเจน | validateEmail() | ทดสอบและปรับปรุงง่าย |
| ลดโค้ดที่ซ้ำกัน | เรียกฟังก์ชันเดิมหลายครั้ง | ลดความคลาดเคลื่อนจากการแก้หลายจุด |
| ตรวจสอบข้อมูลนำเข้า | ตรวจว่าราคาเป็นตัวเลข | ป้องกันข้อมูลผิดรูปแบบ |
| ส่งค่ากลับอย่างสม่ำเสมอ | return object หรือค่าที่กำหนด | ผู้เรียกนำผลลัพธ์ไปใช้ต่อได้ |
11. แบบฝึกหัดท้ายบท
ลองเขียนฟังก์ชันด้วยตนเองก่อนดูแนวทางเฉลย เป้าหมายคือฝึกเลือกพารามิเตอร์ เขียนสูตรภายในฟังก์ชัน และใช้ `return` เพื่อส่งผลลัพธ์กลับ
โจทย์ที่ 1: สร้างฟังก์ชันหาพื้นที่สี่เหลี่ยมผืนผ้า
สร้างฟังก์ชันชื่อ `rectangleArea(width, height)` รับความกว้างและความสูง แล้วส่งค่าพื้นที่กลับมา ทดลองเรียกด้วย `rectangleArea(8, 5)` ซึ่งควรได้ผลลัพธ์ 40
function rectangleArea(width, height) {
return width * height;
}
console.log(rectangleArea(8, 5));
โจทย์ที่ 2: ตรวจสอบว่าตัวเลขเป็นเลขคู่หรือไม่
สร้างฟังก์ชัน `isEven(number)` ที่ส่งค่า `true` เมื่อจำนวนเต็มหารด้วย 2 ลงตัว และส่งค่า `false` เมื่อเป็นเลขคี่ ลองทดสอบด้วยเลข 12 และ 7
function isEven(number) {
return Number.isInteger(number) && number % 2 === 0;
}
console.log(isEven(12));
console.log(isEven(7));
console.log(isEven(4.5));
โจทย์ที่ 3: คำนวณราคาเฉลี่ย
สร้างฟังก์ชัน `average(numbers)` รับ Array ของตัวเลข แล้วส่งค่าเฉลี่ยกลับมา ควรตรวจสอบกรณี Array ว่างเพื่อไม่ให้เกิดการหารด้วยศูนย์
function average(numbers) {
if (numbers.length === 0) {
return null;
}
const total = numbers.reduce((sum, number) => sum + number, 0);
return total / numbers.length;
}
console.log(average([10, 20, 30]));
console.log(average([]));
12. สรุปบทเรียนและเตรียมตัวสู่ตอนถัดไป
ในตอนนี้เราได้เรียนรู้การประกาศฟังก์ชัน การรับพารามิเตอร์ การกำหนดค่าเริ่มต้น การส่งค่าด้วย `return` รูปแบบ Function Expression และ Arrow Function รวมถึงการนำฟังก์ชันไปใช้กับ Array และ Object ความเข้าใจเหล่านี้เป็นพื้นฐานสำคัญของการพัฒนาโปรแกรม JavaScript ที่มีโครงสร้างชัดเจน
เขียนโค้ดให้ทำงานได้คือจุดเริ่มต้น แต่การแบ่งงานให้ชัดเจนและนำกลับมาใช้ซ้ำได้ คือก้าวสำคัญของการเขียนโปรแกรมอย่างมีคุณภาพ
ตอนต่อไป: Scope และ Closureเมื่อรู้จักฟังก์ชันแล้ว ตอนถัดไปจะต่อยอดด้วยเรื่องขอบเขตของตัวแปร (Scope) และ Closure เพื่อทำความเข้าใจว่าฟังก์ชันเข้าถึงข้อมูลจากบริบทภายนอกได้อย่างไร และความรู้นี้นำไปใช้สร้างโครงสร้างโปรแกรมที่ยืดหยุ่นขึ้นได้อย่างไร
ทบทวนด้วยการลองเปลี่ยนค่าที่ส่งเข้าไปในฟังก์ชันทุกตัว แล้วสังเกตผลลัพธ์ที่เปลี่ยนแปลง เมื่อสามารถอธิบายได้ว่าแต่ละฟังก์ชันรับข้อมูลอะไร ประมวลผลอย่างไร และส่งค่าใดกลับมา แสดงว่าคุณเริ่มพร้อมสำหรับการเขียนโปรแกรมที่ซับซ้อนขึ้นแล้ว
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง
⏳ กำลังจับเวลาอ่าน...