02
September 22, 2025

Functions & Scope: หัวใจของ JavaScript ที่หลายคนเข้าใจผิด

Function Declaration & ExpressionArrow FunctionsParameters & Return ValuesScope & HoistingClosures

🎯 สิ่งที่คุณจะได้เรียนในบทนี้

ในบทนี้ คุณจะได้เรียนรู้:

  • 🔧 Functions - วิธีสร้างและใช้งาน functions ทุกรูปแบบ
  • 🏠 Scope & Hoisting - เข้าใจว่าตัวแปรมองเห็นกันยังไง
  • 🎁 Closures - ความลับของการ "จำ" ค่าใน functions
  • 🚀 Real Patterns - เทคนิคที่ใช้จริงในงาน

📝 Outcome

หลังจบบทนี้ คุณจะ:

  • ✅ เขียน functions ได้ทั้ง 3 แบบ และรู้ว่าควรใช้แบบไหนเมื่อไหร่
  • ✅ เข้าใจ scope และ hoisting ไม่งงอีกต่อไป
  • ✅ ใช้ closures สร้าง private variables ได้
  • ✅ เขียน callbacks, debounce, throttle แบบมืออาชีพ
  • ✅ debug functions และแก้ปัญหา this context ได้

🎯 ทำไมต้องเข้าใจ Functions?

🤷 ปัญหาที่เราเจอทุกวัน

ลองนึกภาพว่าคุณต้องคำนวณส่วนลดสินค้า 10% ให้ลูกค้า 5 คน:

javascript
// ❌ วิธีที่น่าเบื่อและเสี่ยงผิดพลาด
const customer1_price = 500;
const customer1_discount = customer1_price * 0.1;
const customer1_final = customer1_price - customer1_discount;
console.log("ลูกค้า 1 จ่าย: " + customer1_final); // 450

const customer2_price = 1200;
const customer2_discount = customer2_price * 0.1;
const customer2_final = customer2_price - customer2_discount;
console.log("ลูกค้า 2 จ่าย: " + customer2_final); // 1080

// ... ทำแบบนี้อีก 3 รอบ 😩

ปัญหาคือ:

  • 😴 เขียนโค้ดซ้ำๆ น่าเบื่อ
  • 🐛 เสี่ยงพิมพ์ผิด เช่น 0.1 เป็น 0.01
  • 🔧 ถ้าเปลี่ยนส่วนลดเป็น 15% ต้องแก้ทุกที่!

✨ Function คือคำตอบ

ลองนึกภาพ Function เป็น "เครื่องจักรอัจฉริยะ":

  • 📥 รับวัตถุดิบ (input/parameters)
  • ⚙️ ประมวลผล (process)
  • 📤 ส่งผลลัพธ์ออกมา (output/return)
javascript
// ✅ สร้าง "เครื่องคิดส่วนลด" ครั้งเดียว
function calculateDiscount(price) {
  const discount = price * 0.1; // คิดส่วนลด 10%
  const finalPrice = price - discount;
  return finalPrice; // ส่งราคาสุดท้ายออกไป
}

// ใช้ซ้ำได้กี่ครั้งก็ได้!
console.log("ลูกค้า 1 จ่าย: " + calculateDiscount(500)); // 450
console.log("ลูกค้า 2 จ่าย: " + calculateDiscount(1200)); // 1080
console.log("ลูกค้า 3 จ่าย: " + calculateDiscount(800)); // 720

ข้อดีคือ:

  • 🎯 เขียนครั้งเดียว ใช้ได้ตลอดชีพ
  • 🛡️ ลดโอกาสผิดพลาด
  • 🔧 แก้ที่เดียว อัพเดททั้งระบบ
  • 📚 อ่านง่าย เข้าใจง่าย

🎩 Functions: กล่องวิเศษที่ทำงานซ้ำได้

🎁 Function คืออะไร?

📖 อธิบายแบบง่ายๆ

Function คือ "กล่องเครื่องมือ" ที่:

  1. 📥 รับของเข้าไป (parameters)
  2. ⚙️ ทำอะไรบางอย่าง (process)
  3. 📤 ส่งผลลัพธ์ออกมา (return)

เหมือนตู้กดน้ำอัตโนมัติ:

  • ใส่เหรียญ = ส่ง parameter
  • เครื่องทำงาน = โค้ดใน function
  • ได้น้ำ = return value

🔍 มาดูโครงสร้างกัน

javascript
// ชื่อ function ควรบอกว่ามันทำอะไร
function ชื่อที่อธิบายการทำงาน(สิ่งที่รับเข้ามา) {
  // ทำอะไรบางอย่าง
  return ผลลัพธ์;
}

🍕 ตัวอย่าง: เครื่องทำพิซซ่า

javascript
// 1️⃣ สร้าง Function (สร้างเครื่องทำพิซซ่า)
function makePizza(topping) {
  // topping = หน้าที่ใส่

  // 2️⃣ ประมวลผล (ทำพิซซ่า)
  const pizza = "พิซซ่า " + topping;

  // 3️⃣ ส่งผลลัพธ์ (เสิร์ฟพิซซ่า)
  return pizza;
}

// 4️⃣ เรียกใช้ Function (สั่งพิซซ่า)
const myPizza = makePizza("ฮาวายเอี้ยน");
console.log(myPizza); // "พิซซ่า ฮาวายเอี้ยน"

🎨 3 วิธีสร้าง Function

1️⃣ Function Declaration (แบบประกาศ)

javascript
// 📣 ประกาศชัดเจนว่าเป็น function
function ทักทาย(ชื่อ) {
  return "สวัสดี " + ชื่อ;
}

console.log(ทักทาย("สมชาย")); // "สวัสดี สมชาย"

// ⭐ ข้อดี: เรียกใช้ก่อนประกาศได้ (Hoisting)
console.log(บอกเวลา()); // ทำงานได้!

function บอกเวลา() {
  return new Date().toLocaleTimeString();
}

2️⃣ Function Expression (แบบนิพจน์)

javascript
// 📦 เก็บ function ในตัวแปร
const คำนวณอายุ = function (ปีเกิด) {
  const ปีนี้ = new Date().getFullYear();
  return ปีนี้ - ปีเกิด;
};

console.log(คำนวณอายุ(2000)); // 25 (ในปี 2025)

// ⚠️ ข้อควรระวัง: ต้องประกาศก่อนใช้
// console.log(อะไรสักอย่าง()); // ❌ Error!
const อะไรสักอย่าง = function () {
  return "ต้องประกาศก่อน";
};

3️⃣ Arrow Function (ลูกศร)

javascript
// 🏹 เขียนสั้น กระชับ
const บวก = (a, b) => a + b;
const กำลังสอง = (x) => x * x;

console.log(บวก(5, 3)); // 8
console.log(กำลังสอง(4)); // 16

// ถ้ามีหลายบรรทัด ต้องใส่ { } และ return
const คิดส่วนลด = (ราคา, เปอร์เซ็นต์) => {
  const ส่วนลด = ราคา * (เปอร์เซ็นต์ / 100);
  const ราคาสุดท้าย = ราคา - ส่วนลด;
  return ราคาสุดท้าย;
};

console.log(คิดส่วนลด(1000, 20)); // 800

🤔 เลือกใช้แบบไหนดี?

แบบเมื่อไหร่ควรใช้ข้อดี
Function Declarationต้องการ hoisting, function หลักๆเรียกก่อนประกาศได้, อ่านง่าย
Function Expressionต้องการเก็บใน variable, ส่งเป็น argumentควบคุมลำดับได้, ยืดหยุ่น
Arrow FunctionCallback functions, ต้องการ this จาก parentเขียนสั้น, this ไม่เปลี่ยน

📥 Parameters vs Arguments: คำที่หลายคนสับสน

📋 ความแตกต่าง

javascript
// Parameters (พารามิเตอร์) = ตัวแปรที่รับค่า (ในวงเล็บตอนประกาศ)
//               ↓    ↓
function สั่งกาแฟ(ชนิด, ขนาด) {
  return `${ชนิด} ${ขนาด}`;
}

// Arguments (อาร์กิวเมนต์) = ค่าที่ส่งเข้าไป (ตอนเรียกใช้)
//       ↓       ↓
สั่งกาแฟ("ลาเต้", "ใหญ่");

ลองนึกเหมือนกรอกฟอร์ม:

  • Parameters = ช่องว่างในฟอร์ม (ชื่อ: _, นามสกุล: _)
  • Arguments = ข้อมูลที่กรอก (ชื่อ: สมชาย, นามสกุล: ใจดี)

🎯 Default Parameters: ค่าเริ่มต้น

javascript
// กำหนดค่าเริ่มต้นได้เลย!
function ทักทายภาษาต่างๆ(ชื่อ = "คุณ", ภาษา = "ไทย") {
  if (ภาษา === "ไทย") {
    return `สวัสดี ${ชื่อ}`;
  } else if (ภาษา === "อังกฤษ") {
    return `Hello ${ชื่อ}`;
  } else {
    return `Bonjour ${ชื่อ}`;
  }
}

console.log(ทักทายภาษาต่างๆ()); // "สวัสดี คุณ"
console.log(ทักทายภาษาต่างๆ("John")); // "สวัสดี John"
console.log(ทักทายภาษาต่างๆ("John", "อังกฤษ")); // "Hello John"

🎁 Rest Parameters: รับค่าไม่จำกัด

javascript
// ใช้ ... (three dots) เพื่อรับค่าหลายๆ ตัว
function รวมตัวเลขทั้งหมด(...ตัวเลข) {
  let ผลรวม = 0;
  for (let เลข of ตัวเลข) {
    ผลรวม += เลข;
  }
  return ผลรวม;
}

console.log(รวมตัวเลขทั้งหมด(1, 2, 3)); // 6
console.log(รวมตัวเลขทั้งหมด(10, 20, 30, 40, 50)); // 150
console.log(รวมตัวเลขทั้งหมด(5)); // 5

🧿 The Mysterious "this": ปริศนาที่หลายคนงง

🎯 this คืออะไร?

this คือการอ้างถึง "เจ้าของ" ของ function ณ ตอนที่เรียกใช้

🏠 this ใน Object

javascript
const ร้านกาแฟ = {
  ชื่อร้าน: "Coffee Paradise",
  เมนู: ["ลาเต้", "คาปูชิโน่", "อเมริกาโน่"],

  แนะนำร้าน() {
    // this = ร้านกาแฟ (object ที่เป็นเจ้าของ method)
    console.log(`ยินดีต้อนรับสู่ ${this.ชื่อร้าน}`);
    console.log(`เรามี ${this.เมนู.length} เมนู`);
  },
};

ร้านกาแฟ.แนะนำร้าน();
// ยินดีต้อนรับสู่ Coffee Paradise
// เรามี 3 เมนู

⚠️ ปัญหา this ที่หลายคนเจอ

javascript
const ปุ่ม = {
  ข้อความ: "คลิกฉันสิ!",

  // ❌ Regular function ใน setTimeout
  แสดงข้อความผิด() {
    setTimeout(function () {
      console.log(this.ข้อความ); // undefined! 😱
      // this ไม่ใช่ปุ่มแล้ว!
    }, 1000);
  },

  // ✅ Arrow function จำ this จาก parent
  แสดงข้อความถูก() {
    setTimeout(() => {
      console.log(this.ข้อความ); // "คลิกฉันสิ!" ✨
      // Arrow function ใช้ this จากข้างนอก
    }, 1000);
  },
};

🎯 วิธีแก้ปัญหา this

javascript
// วิธีที่ 1: เก็บ this ไว้ในตัวแปร
function แบบเก่า() {
  const self = this; // เก็บไว้ก่อน
  setTimeout(function () {
    console.log(self.ข้อความ);
  }, 1000);
}

// วิธีที่ 2: ใช้ bind()
function แบบBind() {
  setTimeout(
    function () {
      console.log(this.ข้อความ);
    }.bind(this),
    1000,
  ); // ผูก this
}

// วิธีที่ 3: ใช้ Arrow Function (แนะนำ!)
function แบบArrow() {
  setTimeout(() => {
    console.log(this.ข้อความ); // ง่ายสุด!
  }, 1000);
}

🚨 Hoisting: สิ่งที่ทำให้งงมาก

🎢 Hoisting คืออะไร?

Hoisting = JavaScript ยก declarations ขึ้นไปด้านบนของ scope โดยอัตโนมัติ

ลองนึกภาพ: คุณเป็นครูเช็คชื่อนักเรียน

javascript
// 🤔 สิ่งที่คุณเขียน
console.log(นักเรียน); // undefined (ไม่ error!)
var นักเรียน = "สมชาย";

// 🔄 สิ่งที่ JavaScript เห็น
var นักเรียน; // ประกาศ (แต่ยังไม่มีค่า)
console.log(นักเรียน); // undefined
นักเรียน = "สมชาย"; // กำหนดค่า

⚡ Hoisting กับ Function Declaration

javascript
// ✅ Function Declaration: Hoisted ทั้งตัว!
ทักทาย(); // "สวัสดี!" - ทำงานได้!

function ทักทาย() {
  console.log("สวัสดี!");
}

// ❌ Function Expression: ไม่ Hoisted
// ลองใช้(); // Error! Cannot access before initialization

const ลองใช้ = function () {
  console.log("ทดสอบ");
};

// ❌ Arrow Function: ไม่ Hoisted เช่นกัน
// ลูกศร(); // Error!

const ลูกศร = () => {
  console.log("Arrow");
};

📋 ตารางสรุป Hoisting

ประเภทHoisted?ใช้ก่อนประกาศได้?
var✅ (แค่ประกาศ)⚠️ (ได้ undefined)
let/const❌ (Error)
Function Declaration✅ (ทั้งตัว)
Function Expression
Arrow Function

🏠 Scope: บ้านของตัวแปร

🏠 Scope คืออะไร?

Scope = พื้นที่ที่ตัวแปรมองเห็นและใช้งานได้

ลองนึกภาพเป็น "บ้าน 3 ชั้น":

javascript
// 🌍 Global Scope (ชั้นดาดฟ้า - เห็นได้ทุกที่)
const ของชั้นดาดฟ้า = "แสงแดด";

function ชั้นที่2() {
  // 🏠 Function Scope (ชั้น 2)
  const ของชั้น2 = "โทรทัศน์";

  if (true) {
    // 📦 Block Scope (ห้องในชั้น 2)
    const ของในห้อง = "เตียง";
    console.log(ของในห้อง); // ✅ เห็น (อยู่ในห้อง)
    console.log(ของชั้น2); // ✅ เห็น (อยู่ชั้นเดียวกัน)
    console.log(ของชั้นดาดฟ้า); // ✅ เห็น (มองขึ้นไป)
  }

  // console.log(ของในห้อง); // ❌ ไม่เห็น! (อยู่นอกห้อง)
}

function ชั้นที่1() {
  // 🏠 Function Scope อื่น (ชั้น 1)
  const ของชั้น1 = "ตู้เย็น";

  // console.log(ของชั้น2); // ❌ ไม่เห็น! (คนละชั้น)
  console.log(ของชั้นดาดฟ้า); // ✅ เห็น (มองขึ้นไป)
}

📊 Scope Chain: การค้นหาตัวแปร

javascript
const ข้อความGlobal = "สวัสดีจาก Global";

function ภายนอก() {
  const ข้อความนอก = "สวัสดีจาก ภายนอก";

  function ภายใน() {
    const ข้อความใน = "สวัสดีจาก ภายใน";

    // 🔍 JavaScript ค้นหาตัวแปรแบบนี้:
    // 1. ในตัวเอง (ภายใน) - เจอ ข้อความใน ✅
    // 2. ถ้าไม่เจอ ดูชั้นนอก (ภายนอก) - เจอ ข้อความนอก ✅
    // 3. ถ้าไม่เจอ ดู Global - เจอ ข้อความGlobal ✅

    console.log(ข้อความใน); // "สวัสดีจาก ภายใน"
    console.log(ข้อความนอก); // "สวัสดีจาก ภายนอก"
    console.log(ข้อความGlobal); // "สวัสดีจาก Global"
  }

  ภายใน();
}

ภายนอก();

⚠️ var vs let/const: ความแตกต่างสำคัญ

javascript
// 🚫 var: Function Scope (ปัญหาเยอะ!)
function ทดสอบVar() {
  if (true) {
    var x = 1; // function scope
  }
  console.log(x); // 1 - เห็นนอก block! 😱
}

// ✅ let/const: Block Scope (แนะนำ!)
function ทดสอบLet() {
  if (true) {
    let y = 1; // block scope
    const z = 2; // block scope
  }
  // console.log(y); // ❌ Error! ไม่เห็นนอก block
  // console.log(z); // ❌ Error! ไม่เห็นนอก block
}

// ปัญหาคลาสสิกของ var ใน loop
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 3, 3, 3 - ทั้งหมดเป็น 3! 😱
  }, 100);
}

// แก้ด้วย let
for (let j = 0; j < 3; j++) {
  setTimeout(() => {
    console.log(j); // 0, 1, 2 - ถูกต้อง! ✅
  }, 100);
}

🔐 Closures: Functions ที่มีความทรงจำ

🎁 Closure คืออะไร?

Closure = function ที่ "จำ" ตัวแปรจาก scope ข้างนอกได้

ลองนึกภาพ: คุณเป็นนักเรียนที่จบการศึกษาแล้ว

  • 🎒 คุณออกจากโรงเรียน (function สิ้นสุด)
  • 📚 แต่ยังจำความรู้ที่เรียนได้ (จำตัวแปรจาก parent scope)
javascript
// 🏫 ตัวอย่าง: โรงเรียนและนักเรียน
function โรงเรียน() {
  const บทเรียน = "JavaScript"; // 📚 ความรู้ในโรงเรียน

  function นักเรียน() {
    // นักเรียนจำบทเรียนได้!
    console.log(`ผมเรียน ${บทเรียน}`);
  }

  return นักเรียน; // นักเรียนจบการศึกษา
}

const ผู้จบการศึกษา = โรงเรียน();
// โรงเรียน() ทำงานจบแล้ว แต่...

ผู้จบการศึกษา(); // "ผมเรียน JavaScript"
// นักเรียนยังจำบทเรียนได้! นี่คือ Closure!

คุณอาจจะคิดว่า:

  • โรงเรียน() ทำงานจบแล้ว
  • ตัวแปร บทเรียน น่าจะหายไป
  • แต่ไม่! function นักเรียน ยังจำได้
  • แม้ออกจากห้องเรียนแล้ว คุณยังมีหนังสือในกระเป๋า!

🔢 ตัวอย่างง่ายๆ: ตัวนับ

javascript
// 🎯 สร้างเครื่องนับที่จำค่าได้
function สร้างตัวนับ() {
  let ค่า = 0; // 🔒 ตัวแปรส่วนตัว (private)

  // function นี้ "จำ" ตัวแปร ค่า ได้!
  return () => {
    ค่า = ค่า + 1;
    return ค่า;
  };
}

// สร้างตัวนับ 2 ตัว
const ตัวนับ1 = สร้างตัวนับ();
const ตัวนับ2 = สร้างตัวนับ();

// แต่ละตัวจำค่าของตัวเองแยกกัน!
console.log(ตัวนับ1()); // 1
console.log(ตัวนับ1()); // 2
console.log(ตัวนับ1()); // 3

console.log(ตัวนับ2()); // 1 (เริ่มนับใหม่!)
console.log(ตัวนับ2()); // 2

// ❌ เข้าถึงตรงๆ ไม่ได้
// console.log(ค่า); // Error! ค่า is not defined

🏦 ตัวอย่าง: ระบบธนาคาร

javascript
// 💰 สร้างบัญชีธนาคารที่ปลอดภัย
function สร้างบัญชีธนาคาร(เงินเริ่มต้น) {
  // 🔒 ยอดเงินเป็นความลับ! (private variable)
  let ยอดเงิน = เงินเริ่มต้น;

  // สร้าง "เครื่อง ATM" ที่เข้าถึงยอดเงินได้
  return {
    // 💵 ฝากเงิน
    ฝาก(จำนวน) {
      if (จำนวน > 0) {
        ยอดเงิน = ยอดเงิน + จำนวน;
        console.log(`✅ ฝาก ${จำนวน} บาท`);
        console.log(`💰 เหลือ ${ยอดเงิน} บาท`);
      } else {
        console.log("❌ จำนวนเงินไม่ถูกต้อง");
      }
    },

    // 💸 ถอนเงิน
    ถอน(จำนวน) {
      if (จำนวน > 0 && จำนวน <= ยอดเงิน) {
        ยอดเงิน = ยอดเงิน - จำนวน;
        console.log(`✅ ถอน ${จำนวน} บาท`);
        console.log(`💰 เหลือ ${ยอดเงิน} บาท`);
      } else {
        console.log("❌ ยอดเงินไม่พอ!");
      }
    },

    // 👀 ดูยอด
    ดูยอด() {
      return `💰 ยอดคงเหลือ ${ยอดเงิน} บาท`;
    },
  };
}

// 🏧 ใช้งานบัญชี
const บัญชีของฉัน = สร้างบัญชีธนาคาร(1000);

บัญชีของฉัน.ฝาก(500); // ✅ ฝาก 500 บาท, 💰 เหลือ 1500 บาท
บัญชีของฉัน.ถอน(200); // ✅ ถอน 200 บาท, 💰 เหลือ 1300 บาท
บัญชีของฉัน.ถอน(2000); // ❌ ยอดเงินไม่พอ!

console.log(บัญชีของฉัน.ดูยอด()); // 💰 ยอดคงเหลือ 1300 บาท

// ❌ แฮกไม่ได้! ยอดเงินถูกซ่อนไว้
console.log(บัญชีของฉัน.ยอดเงิน); // undefined
// console.log(ยอดเงิน);       // Error!

🚀 Higher-Order Functions: Functions ที่เล่นกับ Functions

🤔 ทำไมต้องใช้ Higher-Order Functions?

ลองนึกภาพ: คุณเป็นเจ้าของร้านกาแฟ

javascript
// ❌ แบบเก่า: เขียนซ้ำๆ
function ทำกาแฟร้อน() {
  console.log("☕ บดเมล็ดกาแฟ");
  console.log("☕ ชงกาแฟร้อน");
  console.log("☕ เสิร์ฟในแก้ว");
}

function ทำกาแฟเย็น() {
  console.log("🧊 บดเมล็ดกาแฟ");
  console.log("🧊 ชงกาแฟร้อน");
  console.log("🧊 ใส่น้ำแข็ง");
  console.log("🧊 เสิร์ฟในแก้วพลาสติก");
}

// มีขั้นตอนซ้ำๆ ต้องเขียนใหม่ทุกครั้ง!

✅ แบบใหม่: ใช้ Higher-Order Function

javascript
// 🎯 สร้าง "เครื่องทำกาแฟ" ที่รับ "วิธีทำ" มาเป็น parameter
function ทำกาแฟ(วิธีเสิร์ฟ) {
  console.log("☕ บดเมล็ดกาแฟ");
  console.log("☕ ชงกาแฟร้อน");

  // เรียกใช้ "วิธีเสิร์ฟ" ที่รับเข้ามา
  วิธีเสิร์ฟ();
}

// สร้างวิธีเสิร์ฟต่างๆ
const เสิร์ฟร้อน = () => console.log("☕ เสิร์ฟในแก้ว");
const เสิร์ฟเย็น = () => {
  console.log("🧊 ใส่น้ำแข็ง");
  console.log("🧊 เสิร์ฟในแก้วพลาสติก");
};

// ใช้งาน: ส่งวิธีเสิร์ฟเข้าไป
ทำกาแฟ(เสิร์ฟร้อน); // ทำกาแฟร้อน
ทำกาแฟ(เสิร์ฟเย็น); // ทำกาแฟเย็น

📚 Higher-Order Function คืออะไร?

พูดง่ายๆ คือ Function ที่:

  1. รับ function อื่นเป็น parameter (เหมือนรับคำสั่ง)
  2. คืนค่าเป็น function (เหมือนสร้างเครื่องจักรใหม่)

🎮 ลองทำดู: Function ที่รับ Function

javascript
// 🔁 สร้าง function ทำซ้ำ
function ทำซ้ำ(จำนวนครั้ง, สิ่งที่จะทำ) {
  for (let i = 0; i < จำนวนครั้ง; i++) {
    สิ่งที่จะทำ(i + 1); // ส่ง "ครั้งที่" ไปด้วย
  }
}

// ใช้งาน
ทำซ้ำ(3, (ครั้งที่) => {
  console.log(`🏃 วิ่งรอบที่ ${ครั้งที่}`);
});
// 🏃 วิ่งรอบที่ 1
// 🏃 วิ่งรอบที่ 2
// 🏃 วิ่งรอบที่ 3

ทำซ้ำ(5, (ครั้งที่) => {
  console.log(`📖 อ่านหน้าที่ ${ครั้งที่}`);
});
// 📖 อ่านหน้าที่ 1
// 📖 อ่านหน้าที่ 2
// ... ถึงหน้าที่ 5

🏭 Function ที่คืน Function: สร้างเครื่องจักร

javascript
// 🎯 โรงงานสร้างเครื่องคูณ
function สร้างเครื่องคูณ(ตัวคูณ) {
  // คืนค่าเป็น function ใหม่!
  return (ตัวตั้ง) => ตัวตั้ง * ตัวคูณ;
}

// สร้างเครื่องคูณต่างๆ
const คูณ2 = สร้างเครื่องคูณ(2);
const คูณ10 = สร้างเครื่องคูณ(10);
const คูณ100 = สร้างเครื่องคูณ(100);

// ใช้งาน
console.log(คูณ2(5)); // 10  (5 x 2)
console.log(คูณ10(5)); // 50  (5 x 10)
console.log(คูณ100(5)); // 500 (5 x 100)

// 💡 สะดวกสำหรับแปลงหน่วย!
const บาทเป็นดอลลาร์ = สร้างเครื่องคูณ(0.03);
const กิโลเป็นปอนด์ = สร้างเครื่องคูณ(2.2);

console.log(บาทเป็นดอลลาร์(1000)); // 30 ดอลลาร์
console.log(กิโลเป็นปอนด์(5)); // 11 ปอนด์

🔍 ตัวอย่างจากชีวิตจริง: ระบบ Log

javascript
// 📝 สร้างระบบ log แบบ Curry
function สร้างLogger(ระดับ) => (ระบบ) => (ข้อความ) => {
  const เวลา = new Date().toLocaleTimeString();
  console.log(`[${เวลา}] [${ระดับ}] [${ระบบ}] ${ข้อความ}`);
};

// สร้าง logger สำหรับแต่ละระบบ
const logDatabase = สร้างLogger("INFO")("Database");
const logAPI = สร้างLogger("INFO")("API");
const logError = สร้างLogger("ERROR");

// ใช้งาน
logDatabase("เชื่อมต่อสำเร็จ");
// [10:30:00] 🔵 [INFO] [Database] เชื่อมต่อสำเร็จ

logAPI("รับ request จาก client");
// [10:30:01] 🔵 [INFO] [API] รับ request จาก client

logError("Payment")("ชำระเงินไม่สำเร็จ");
// [10:30:02] 🔴 [ERROR] [Payment] ชำระเงินไม่สำเร็จ

📞 Callback Pattern: ส่ง Function ไปทำงานให้

🤔 Callback คืออะไร?

ลองนึกภาพ: คุณสั่งพิซซ่า

  1. 📞 โทรสั่งพิซซ่า
  2. 🍕 ร้านทำพิซซ่า (ใช้เวลา)
  3. 🛵 ส่งถึงบ้าน
  4. 🔔 กดกริ่ง -> นี่คือ Callback!

Callback คือ function ที่เราส่งไป และจะถูกเรียกเมื่อ "งานเสร็จ"

📱 ตัวอย่าง: รอข้อมูลจาก Server

javascript
// 🌐 จำลองการดึงข้อมูลจาก server
function ดึงข้อมูลผู้ใช้(userId, เมื่อเสร็จแล้ว) {
  console.log("🔄 กำลังดึงข้อมูล...");

  // จำลองว่ารอ server ตอบ 2 วินาที
  setTimeout(() => {
    const ข้อมูล = {
      id: userId,
      ชื่อ: "สมชาย",
      อีเมล: "somchai@example.com",
    };

    // เรียก callback เมื่อได้ข้อมูล!
    เมื่อเสร็จแล้ว(ข้อมูล);
  }, 2000);
}

// ใช้งาน: บอกว่าจะทำอะไรเมื่อได้ข้อมูล
ดึงข้อมูลผู้ใช้(123, (ผลลัพธ์) => {
  console.log("✅ ได้ข้อมูลแล้ว!");
  console.log("👤 ชื่อ:", ผลลัพธ์.ชื่อ);
  console.log("📧 อีเมล:", ผลลัพธ์.อีเมล);
});

// โปรแกรมทำงานต่อได้ ไม่ต้องรอ!
console.log("⏳ ทำอย่างอื่นระหว่างรอ...");

🖱️ Event Handlers: Callback ที่ใช้บ่อยที่สุด

javascript
// 🔘 เมื่อคลิกปุ่ม
const ปุ่ม = document.querySelector("#myButton");

ปุ่ม.addEventListener("click", (event) => {
  console.log("🖱️ คุณคลิกปุ่ม!");
  console.log("📍 ตำแหน่งที่คลิก:", event.clientX, event.clientY);
});

// 📝 เมื่อพิมพ์ในช่องค้นหา
const ช่องค้นหา = document.querySelector("#search");

ช่องค้นหา.addEventListener("input", (event) => {
  const คำค้นหา = event.target.value;
  console.log("🔍 กำลังค้นหา:", คำค้นหา);
  // ค้นหาข้อมูลตามคำที่พิมพ์
});

// 📜 เมื่อเลื่อนหน้า
window.addEventListener("scroll", () => {
  const เลื่อนไปแล้ว = window.scrollY;
  console.log("📜 เลื่อนหน้าไป:", เลื่อนไปแล้ว, "pixels");
});

🎯 Array Methods: Callback สำหรับจัดการข้อมูล

javascript
// มีรายการสินค้า
const สินค้า = [
  { ชื่อ: "กาแฟ", ราคา: 60, หมวดหมู่: "เครื่องดื่ม" },
  { ชื่อ: "ขนมปัง", ราคา: 25, หมวดหมู่: "อาหาร" },
  { ชื่อ: "ชาเขียว", ราคา: 45, หมวดหมู่: "เครื่องดื่ม" },
  { ชื่อ: "คุกกี้", ราคา: 35, หมวดหมู่: "อาหาร" },
];

// 🔍 filter: เลือกเฉพาะที่ต้องการ
const เครื่องดื่ม = สินค้า.filter((รายการ) => {
  return รายการ.หมวดหมู่ === "เครื่องดื่ม";
});
console.log("☕ เครื่องดื่ม:", เครื่องดื่ม);

// 🔄 map: แปลงข้อมูลทุกตัว
const ชื่อสินค้า = สินค้า.map((รายการ) => {
  return รายการ.ชื่อ;
});
console.log("📝 รายชื่อ:", ชื่อสินค้า);
// ["กาแฟ", "ขนมปัง", "ชาเขียว", "คุกกี้"]

// ➕ reduce: รวมเป็นค่าเดียว
const ราคารวม = สินค้า.reduce((ยอดรวม, รายการ) => {
  return ยอดรวม + รายการ.ราคา;
}, 0);
console.log("💰 ราคารวม:", ราคารวม, "บาท"); // 165 บาท

🛠️ สร้าง Callback ของเราเอง

javascript
// 🎬 function ที่ทำงานเป็นขั้นตอน
function ทำอาหาร(เมนู, เริ่มทำ, ทำเสร็จ, เกิดข้อผิดพลาด) {
  console.log(`🍳 เริ่มทำ ${เมนู}...`);

  // เรียก callback "เริ่มทำ"
  เริ่มทำ(เมนู);

  // จำลองการทำอาหาร
  setTimeout(() => {
    // สุ่มว่าจะสำเร็จหรือไม่
    const สำเร็จ = Math.random() > 0.3;

    if (สำเร็จ) {
      // เรียก callback "ทำเสร็จ"
      ทำเสร็จ(`${เมนู} พร้อมเสิร์ฟ!`);
    } else {
      // เรียก callback "เกิดข้อผิดพลาด"
      เกิดข้อผิดพลาด(`${เมนู} ไหม้!`);
    }
  }, 2000);
}

// ใช้งาน
ทำอาหาร(
  "ข้าวผัด",
  (เมนู) => console.log(`⏰ ตั้งเวลาทำ ${เมนู}`),
  (ผลลัพธ์) => console.log(`✅ ${ผลลัพธ์}`),
  (ข้อผิดพลาด) => console.log(`❌ ${ข้อผิดพลาด}`),
);

⚡ IIFE: Function ที่รันเองทันที

🤔 IIFE คืออะไร?

IIFE = Immediately Invoked Function Expression (อ่านว่า "อิฟฟี่")

ลองนึกภาพ: เหมือนระเบิดเวลาที่ตั้งไว้ 0 วินาที - ระเบิดทันที!

javascript
// 💣 Function ปกติ: ต้องเรียกถึงจะทำงาน
function ทักทาย() {
  console.log("สวัสดี!");
}
ทักทาย(); // ต้องเรียก

// 🎆 IIFE: ทำงานเองทันที!
(() => {
  console.log("สวัสดี!");
})(); // <- วงเล็บท้ายทำให้รันทันที!

🔨 วิธีสร้าง IIFE

javascript
// 📝 รูปแบบที่ 1: Arrow Function
(() => {
  console.log("🚀 รันทันที แบบที่ 1");
})();

// 📝 รูปแบบที่ 2: Regular Function
(function () {
  console.log("🚀 รันทันที แบบที่ 2");
})();

// 📝 รูปแบบที่ 3: รับค่าได้ด้วย!
((ชื่อ) => {
  console.log(`🚀 สวัสดี ${ชื่อ}!`);
})("สมชาย"); // ส่งค่าเข้าไป

🏠 ใช้ IIFE ทำอะไรได้?

1️⃣ สร้างพื้นที่ส่วนตัว (Private Space)

javascript
// ❌ ปัญหา: ตัวแปรรั่วไหลออกมา global
var คะแนน = 0;
var ชีวิต = 3;
// คนอื่นแก้ได้!
คะแนน = 999999; // โกง!

// ✅ ใช้ IIFE: ซ่อนตัวแปร
(() => {
  let คะแนน = 0;
  let ชีวิต = 3;

  // เปิดเฉพาะที่ต้องการให้ใช้
  window.เกม = {
    เพิ่มคะแนน() {
      คะแนน += 10;
      console.log(`⭐ คะแนน: ${คะแนน}`);
    },
    ดูคะแนน() {
      return คะแนน;
    },
  };
})();

// ใช้ได้เฉพาะที่เปิดให้
เกม.เพิ่มคะแนน(); // ⭐ คะแนน: 10
console.log(เกม.ดูคะแนน()); // 10

// แฮกไม่ได้!
console.log(คะแนน); // Error: คะแนน is not defined

2️⃣ ทำงานครั้งเดียวตอนเริ่ม

javascript
// 🎬 Setup เริ่มต้นของเว็บ
(() => {
  console.log("🌟 กำลังเริ่มต้นระบบ...");

  // ตรวจสอบ browser
  if (!window.localStorage) {
    alert("❌ Browser ไม่รองรับ localStorage");
    return;
  }

  // โหลดค่าเริ่มต้น
  const การตั้งค่า = localStorage.getItem("settings") || "{}";
  const ค่าเริ่มต้น = JSON.parse(การตั้งค่า);

  // ตั้งค่า theme
  if (ค่าเริ่มต้น.darkMode) {
    document.body.classList.add("dark-mode");
  }

  console.log("✅ ระบบพร้อมใช้งาน!");
})();

3️⃣ ป้องกันตัวแปรชนกัน

javascript
// 🏢 Plugin A
(() => {
  const version = "1.0";
  const name = "PluginA";

  window.PluginA = {
    info() {
      return `${name} v${version}`;
    },
  };
})();

// 🏢 Plugin B
(() => {
  const version = "2.0"; // ไม่ชนกับ Plugin A!
  const name = "PluginB";

  window.PluginB = {
    info() {
      return `${name} v${version}`;
    },
  };
})();

console.log(PluginA.info()); // PluginA v1.0
console.log(PluginB.info()); // PluginB v2.0

🆚 IIFE vs Modern Modules

javascript
// 📦 วิธีสมัยใหม่: ES6 Modules
// file: utils.js
const ตัวแปรส่วนตัว = "ซ่อนอยู่ใน module";

export function ฟังก์ชันสาธารณะ() {
  console.log("สามารถใช้จากข้างนอกได้");
}

// file: main.js
import { ฟังก์ชันสาธารณะ } from "./utils.js";

🌟 Real Patterns ที่ใช้จริงในงาน

🤔 ทำไมต้องใช้ Patterns พวกนี้?

ลองนึกภาพ: คุณทำช่องค้นหา

  • ผู้ใช้พิมพ์ "c" -> เรียก API
  • พิมพ์ "o" -> เรียก API อีก
  • พิมพ์ "f" -> เรียก API อีก
  • พิมพ์ "f" -> เรียก API อีก
  • พิมพ์ "e" -> เรียก API อีก
  • พิมพ์ "e" -> เรียก API อีก

ผู้ใช้แค่พิมพ์คำว่า "coffee" แต่เรียก API ไป 6 ครั้ง! 💸

⏰ Debounce: รอให้หยุดทำก่อนค่อยตอบสนอง

Debounce = รอให้หยุดทำก่อน แล้วค่อยตอบสนอง

เหมือนลิฟต์ที่รอคนขึ้นให้ครบก่อนค่อยปิดประตู!

javascript
// 🎯 สร้าง Debounce function
function debounce(ฟังก์ชันที่จะทำ, เวลารอ) {
  let ตัวจับเวลา;

  return function (...ค่าที่ส่งมา) {
    // ❌ ยกเลิกตัวจับเวลาเก่า (ถ้ามี)
    clearTimeout(ตัวจับเวลา);

    // ⏰ ตั้งเวลาใหม่
    ตัวจับเวลา = setTimeout(() => {
      // ✅ ทำงานจริงเมื่อหมดเวลา
      ฟังก์ชันที่จะทำ.apply(this, ค่าที่ส่งมา);
    }, เวลารอ);
  };
}

// 💼 ใช้งานจริง: ช่องค้นหา
const ช่องค้นหา = document.querySelector("#search");

// สร้าง function ที่มี debounce
const ค้นหาอัจฉริยะ = debounce((event) => {
  const คำค้นหา = event.target.value;
  console.log("🔍 ค้นหา:", คำค้นหา);

  // เรียก API ตรงนี้
  // fetch(`/api/search?q=${คำค้นหา}`)...
}, 500); // รอ 500ms หลังหยุดพิมพ์

ช่องค้นหา.addEventListener("input", ค้นหาอัจฉริยะ);

// 📝 ผู้ใช้พิมพ์:
// c     -> เริ่มจับเวลา 500ms
// co    -> ยกเลิกเก่า, เริ่มใหม่ 500ms
// cof   -> ยกเลิกเก่า, เริ่มใหม่ 500ms
// coff  -> ยกเลิกเก่า, เริ่มใหม่ 500ms
// coffe -> ยกเลิกเก่า, เริ่มใหม่ 500ms
// coffee -> ยกเลิกเก่า, เริ่มใหม่ 500ms
// [หยุดพิมพ์ 500ms]
// ✅ ค้นหา: "coffee" (เรียก API แค่ครั้งเดียว!)

🚦 Throttle: จำกัดความถี่การทำงาน

Throttle = ทำได้แค่ตามจังหวะที่กำหนด

เหมือนไฟจราจร - เขียวทุก 30 วินาที ไม่ว่าจะมีรถรอกี่คัน!

javascript
// 🎯 สร้าง Throttle function
function throttle(ฟังก์ชันที่จะทำ, ระยะเวลา) {
  let กำลังรอ = false;

  return function (...ค่าที่ส่งมา) {
    // 🔒 ถ้ากำลังรออยู่ = ข้าม
    if (กำลังรอ) return;

    // ✅ ทำงานได้!
    ฟังก์ชันที่จะทำ.apply(this, ค่าที่ส่งมา);

    // 🚫 ปิดไม่ให้ทำงานชั่วคราว
    กำลังรอ = true;

    // ⏰ เปิดให้ทำงานอีกครั้งเมื่อครบเวลา
    setTimeout(() => {
      กำลังรอ = false;
    }, ระยะเวลา);
  };
}

// 💼 ใช้งานจริง: ติดตาม scroll
const ติดตามScroll = throttle(() => {
  const ตำแหน่ง = window.scrollY;
  const เปอร์เซ็นต์ = Math.round((ตำแหน่ง / document.body.scrollHeight) * 100);

  console.log(`📜 เลื่อนไปแล้ว ${เปอร์เซ็นต์}%`);

  // อัปเดต progress bar
  // updateProgressBar(เปอร์เซ็นต์);
}, 200); // ทำงานได้ทุก 200ms (5 ครั้ง/วินาที)

window.addEventListener("scroll", ติดตามScroll);

// 📝 ผลลัพธ์เมื่อเลื่อนหน้าเร็วๆ:
// [0ms]    📜 เลื่อนไปแล้ว 5%    ✅ ทำงาน
// [50ms]   (เลื่อน)              ❌ ข้าม (ยังไม่ครบ 200ms)
// [100ms]  (เลื่อน)              ❌ ข้าม
// [150ms]  (เลื่อน)              ❌ ข้าม
// [200ms]  📜 เลื่อนไปแล้ว 25%   ✅ ทำงาน
// [250ms]  (เลื่อน)              ❌ ข้าม

🤔 Debounce vs Throttle ใช้อันไหนดี?

DebounceThrottle
รอให้หยุดทำก่อนค่อยตอบสนองทำงานเป็นจังหวะสม่ำเสมอ
✅ ช่องค้นหา✅ Scroll tracking
✅ ปุ่ม Save อัตโนมัติ✅ ปรับขนาดหน้าต่าง
✅ ตรวจสอบ username✅ อัปเดต progress
"รอให้นิ่งก่อนค่อยทำ""ทำเป็นระยะๆ ไม่ถี่เกิน"

🍛 Currying: แบ่ง Parameters เป็นขั้นๆ

Currying = แปลง function ที่รับหลายค่า ให้รับทีละค่า

เหมือนร้านกาแฟที่มี "เมนูพิเศษประจำ":

  • ลูกค้าประจำ: "เอาแบบเดิม" (บันทึกไว้แล้ว)
  • ลูกค้าใหม่: ต้องบอกทุกอย่าง
javascript
// 🍳 ตัวอย่างง่ายๆ: ร้านกาแฟ

// ❌ แบบเดิม: ต้องบอกทุกอย่างทุกครั้ง
function สั่งกาแฟ(ขนาด, ชนิด, ความหวาน, ชื่อลูกค้า) {
  return `☕ ${ชนิด} ${ขนาด} หวาน ${ความหวาน}% สำหรับคุณ ${ชื่อลูกค้า}`;
}

// ต้องพิมพ์ซ้ำๆ
สั่งกาแฟ("ใหญ่", "ลาเต้", 50, "สมชาย");
สั่งกาแฟ("ใหญ่", "ลาเต้", 50, "สมหญิง"); // ซ้ำ!
สั่งกาแฟ("ใหญ่", "ลาเต้", 50, "สมศักดิ์"); // ซ้ำอีก!

// ✅ ใช้ Currying: สร้างเมนูพิเศษ
const สร้างเมนูพิเศษ = (ขนาด) => (ชนิด) => (ความหวาน) => (ชื่อลูกค้า) => {
  return `☕ ${ชนิด} ${ขนาด} หวาน ${ความหวาน}% สำหรับคุณ ${ชื่อลูกค้า}`;
};

// สร้างเมนูโปรด
const ลาเต้ใหญ่หวาน50 = สร้างเมนูพิเศษ("ใหญ่")("ลาเต้")(50);

// ใช้ง่ายขึ้นมาก!
console.log(ลาเต้ใหญ่หวาน50("สมชาย"));
console.log(ลาเต้ใหญ่หวาน50("สมหญิง"));
console.log(ลาเต้ใหญ่หวาน50("สมศักดิ์"));

💼 ตัวอย่างจากงานจริง: ระบบ Log

javascript
// 📝 สร้าง Logger แบบ Curry
const createLogger = (ระดับ) => (ระบบ) => (ข้อความ) => {
  const เวลา = new Date().toLocaleTimeString();
  const สี = {
    INFO: "🔵",
    WARN: "🟡",
    ERROR: "🔴",
  };

  console.log(`[${เวลา}] ${สี[ระดับ]} [${ระดับ}] [${ระบบ}] ${ข้อความ}`);
};

// สร้าง logger สำหรับแต่ละระบบ
const logDatabase = createLogger("INFO")("Database");
const logAPI = createLogger("INFO")("API");
const logError = createLogger("ERROR");

// ใช้งาน
logDatabase("เชื่อมต่อสำเร็จ");
// [10:30:00] 🔵 [INFO] [Database] เชื่อมต่อสำเร็จ

logAPI("รับ request จาก client");
// [10:30:01] 🔵 [INFO] [API] รับ request จาก client

logError("Payment")("ชำระเงินไม่สำเร็จ");
// [10:30:02] 🔴 [ERROR] [Payment] ชำระเงินไม่สำเร็จ

❌ Common Mistakes: ข้อผิดพลาดที่ทุกคนเคยเจอ

😵 Mistake #1: ลืม return

ปัญหา: Function ทำงานแล้วแต่ไม่ได้ค่า

javascript
// ❌ ลืม return
function คำนวณราคารวม(ราคา, จำนวน) {
  const รวม = ราคา * จำนวน;
  // คำนวณแล้วแต่ไม่ได้ส่งค่ากลับ!
}

const ยอดชำระ = คำนวณราคารวม(100, 3);
console.log(ยอดชำระ); // undefined 😭

// ✅ แก้ไข: ใส่ return
function คำนวณราคารวม(ราคา, จำนวน) {
  return ราคา * จำนวน; // ไม่ลืมแล้ว!
}

console.log(คำนวณราคารวม(100, 3)); // 300 🎉

🤷 Mistake #2: this หายไปไหน?

ปัญหา: Regular function ใน setTimeout ทำให้ this หาย

javascript
// ❌ this หาย
const ผู้ใช้ = {
  ชื่อ: "สมชาย",

  ทักทายหลังจากนี้() {
    setTimeout(function () {
      // 🚨 regular function ใน setTimeout
      console.log(`สวัสดี ${this.ชื่อ}`);
      // สวัสดี undefined 😱
    }, 1000);
  },
};

// ✅ แก้ไข: ใช้ Arrow Function
const ผู้ใช้ = {
  ชื่อ: "สมชาย",

  ทักทายหลังจากนี้() {
    setTimeout(() => {
      // ✨ arrow function จำ this ได้!
      console.log(`สวัสดี ${this.ชื่อ}`);
      // สวัสดี สมชาย ✅
    }, 1000);
  },
};

👾 Mistake #3: แก้ไข Array ต้นฉบับ

ปัญหา: แก้ไข parameter โดยตรง ทำให้ข้อมูลเดิมเปลี่ยน

javascript
// ❌ แก้ไขข้อมูลเดิม (Mutate)
function เพิ่มรายการ(ตะกร้า, สินค้า) {
  ตะกร้า.push(สินค้า); // แก้ไข array เดิม!
  return ตะกร้า;
}

const ตะกร้าเดิม = ["🍎", "🍌"];
const ตะกร้าใหม่ = เพิ่มรายการ(ตะกร้าเดิม, "🍓");

console.log(ตะกร้าเดิม); // ["🍎", "🍌", "🍓"] - เอ้า! เปลี่ยนไปด้วย!

// ✅ แก้ไข: สร้าง Array ใหม่ (Immutable)
function เพิ่มรายการ(ตะกร้า, สินค้า) {
  return [...ตะกร้า, สินค้า]; // สร้างใหม่!
}

const ตะกร้าเดิม = ["🍎", "🍌"];
const ตะกร้าใหม่ = เพิ่มรายการ(ตะกร้าเดิม, "🍓");

console.log(ตะกร้าเดิม); // ["🍎", "🍌"] - เดิมไม่เปลี่ยน! ✅
console.log(ตะกร้าใหม่); // ["🍎", "🍌", "🍓"]

😕 Mistake #4: Parameters เยอะเกิน

ปัญหา: เรียกใช้ยาก จำลำดับไม่ได้

javascript
// ❌ Parameters เยอะเกิน
function สร้างผู้ใช้(
  ชื่อ,
  อายุ,
  อีเมล,
  เบอร์,
  ที่อยู่,
  ตำแหน่ง,
  แผนก,
  เงินเดือน,
) {
  // จำไม่ได้ว่าอันไหนคืออะไร!
}

// เรียกใช้:
สร้างผู้ใช้(
  "สมชาย",
  25,
  "somchai@email.com",
  "081234567",
  "123 ถ.สุขุมวิท",
  "Developer",
  "IT",
  35000,
); // อันไหนคืออะไร? 😵

// ✅ แก้ไข: ใช้ Object Parameter
function สร้างผู้ใช้({ ชื่อ, อายุ, อีเมล, เบอร์, ที่อยู่, ตำแหน่ง }) {
  // ชัดเจนกว่าเดิมมาก!
  console.log(`👤 ชื่อ: ${ชื่อ}`);
  console.log(`🎂 อายุ: ${อายุ}`);
  // ฯลฯ
}

// เรียกใช้: ชัดเจนมาก!
สร้างผู้ใช้({
  ชื่อ: "สมชาย",
  อายุ: 25,
  อีเมล: "somchai@email.com",
  // ข้ามบางค่าได้ด้วย!
});

🔍 Debugging Functions: วิธีหา Bug ให้เจอ

🤔 ทำไมต้อง Debug?

ลองนึกภาพ: คุณเป็นนักสืบที่ต้องหาว่าทำไมโค้ดไม่ทำงาน

📊 ดูข้อมูล Function

javascript
// 🔍 ดูคุณสมบัติของ function
function คำนวณภาษี(ราคา, อัตราภาษี) {
  return ราคา * (อัตราภาษี / 100);
}

// ดูชื่อ function
console.log("🏷️ ชื่อ:", คำนวณภาษี.name);
// 🏷️ ชื่อ: คำนวณภาษี

// ดูจำนวน parameters
console.log("🔢 รับกี่ค่า:", คำนวณภาษี.length);
// 🔢 รับกี่ค่า: 2

// ดู source code
console.log("📝 โค้ด:", คำนวณภาษี.toString());

🔬 ใช้ debugger ดู Scope

javascript
// 🏠 ตัวอย่าง Scope ซ้อนกัน
function ภายนอก() {
  const ข้อความนอก = "🌳 ฉันอยู่ข้างนอก";
  const ค่าคงที่ = 100;

  function ภายใน() {
    const ข้อความใน = "🏠 ฉันอยู่ข้างใน";

    // 🛑 หยุดตรงนี้! เปิด DevTools ดู Scope
    debugger;

    // ใน DevTools จะเห็น:
    // Local: { ข้อความใน }
    // Closure: { ข้อความนอก, ค่าคงที่ }
    // Global: { window, document, ... }

    console.log(ข้อความใน);
    console.log(ข้อความนอก);
    console.log(ค่าคงที่);
  }

  ภายใน();
}

ภายนอก();
// เปิด Console และดู Sources tab ใน DevTools

📈 ติดตาม Function Calls

javascript
// 🎯 สร้าง Function Tracker
function ติดตามFunction(functionToTrack) {
  let จำนวนครั้ง = 0;
  let ประวัติ = [];

  return function (...args) {
    จำนวนครั้ง++;
    const เวลา = new Date().toLocaleTimeString();

    console.log(`🔄 เรียกครั้งที่ ${จำนวนครั้ง}`);
    console.log(`⏰ เวลา: ${เวลา}`);
    console.log(`📥 ค่าที่ส่งมา:`, args);

    // เรียก function จริง
    const ผลลัพธ์ = functionToTrack.apply(this, args);

    console.log(`📤 ผลลัพธ์:`, ผลลัพธ์);
    console.log("-".repeat(30));

    // เก็บประวัติ
    ประวัติ.push({ เวลา, args, ผลลัพธ์ });

    return ผลลัพธ์;
  };
}

// ใช้งาน
const บวก = (a, b) => a + b;
const บวกแบบติดตาม = ติดตามFunction(บวก);

บวกแบบติดตาม(5, 3);
// 🔄 เรียกครั้งที่ 1
// ⏰ เวลา: 10:30:00
// 📥 ค่าที่ส่งมา: [5, 3]
// 📤 ผลลัพธ์: 8

บวกแบบติดตาม(10, 20);
// 🔄 เรียกครั้งที่ 2
// ...

🐛 เทคนิคหา Bug เกี่ยวกับ Scope

javascript
// 🔍 ตรวจสอบว่าตัวแปรอยู่ scope ไหน
function ตรวจสอบScope() {
  const ตัวแปรLocal = "ฉันอยู่ใน function";

  console.log("🏠 Local:", ตัวแปรLocal);

  // ตรวจสอบ global
  if (typeof window !== "undefined") {
    console.log("🌍 Global มี window");
  }

  // ตรวจสอบว่าตัวแปรมีจริงไหม
  try {
    console.log(ตัวแปรที่ไม่มี);
  } catch (error) {
    console.log("❌ ตัวแปรนี้ไม่มี!");
  }
}

ตรวจสอบScope();

⚡ Performance Tips: เทคนิคทำให้โค้ดเร็วขึ้น

🚫 อย่าสร้าง Function ใน Loop

ปัญหา: สร้าง function ใหม่ซ้ำๆ กิน memory

javascript
// ❌ แย่มาก: สร้าง 1000 functions!
const ปุ่มทั้งหมด = document.querySelectorAll(".button");

for (let i = 0; i < ปุ่มทั้งหมด.length; i++) {
  ปุ่มทั้งหมด[i].addEventListener("click", () => {
    // สร้าง function ใหม่ทุกรอบ!
    console.log(`คลิกปุ่ม ${i}`);
  });
}
// ถ้ามี 1000 ปุ่ม = สร้าง 1000 functions! 💣

// ✅ ดี: ใช้ function เดียว
function จัดการคลิก(event) {
  // ดึงข้อมูลจาก data attribute
  const หมายเลข = event.target.dataset.index;
  console.log(`คลิกปุ่ม ${หมายเลข}`);
}

for (let i = 0; i < ปุ่มทั้งหมด.length; i++) {
  ปุ่มทั้งหมด[i].dataset.index = i;
  ปุ่มทั้งหมด[i].addEventListener("click", จัดการคลิก);
}
// ใช้ function เดียว = ประหยัด memory! 🎉

🧠 Memoization: จำคำตอบไว้

คืออะไร? เหมือนนักเรียนที่จดคำตอบ - ถ้าเคยคิดแล้วไม่ต้องคิดใหม่!

javascript
// 🎯 สร้าง Memoization
function memoize(functionToCache) {
  const กล่องเก็บคำตอบ = new Map();

  return function (...args) {
    // สร้าง "กุญแจ" จาก arguments
    const กุญแจ = JSON.stringify(args);

    // 🔍 เคยคิดแล้ว?
    if (กล่องเก็บคำตอบ.has(กุญแจ)) {
      console.log("📦 เจอในกล่อง!");
      return กล่องเก็บคำตอบ.get(กุญแจ);
    }

    // 🧮 ไม่เคยคิด - คิดใหม่
    console.log("🧮 คิดใหม่...");
    const คำตอบ = functionToCache.apply(this, args);

    // 💾 เก็บไว้ในกล่อง
    กล่องเก็บคำตอบ.set(กุญแจ, คำตอบ);

    return คำตอบ;
  };
}

// 🎮 ตัวอย่าง: คำนวณ Fibonacci
function fibonacci(n) {
  // คำนวณนานมากเมื่อ n ใหญ่
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

// ใช้ memoization
const fibonacciฉลาด = memoize(fibonacci);

console.log(fibonacciฉลาด(10));
// 🧮 คิดใหม่... -> 55

console.log(fibonacciฉลาด(10));
// 📦 เจอในกล่อง! -> 55 (เร็วมาก!)

console.log(fibonacciฉลาด(20));
// 🧮 คิดใหม่... -> 6765

🎯 Event Delegation: ใช้ Event ร่วมกัน

javascript
// ❌ แย่: ใส่ listener ทุกตัว
const รายการทั้งหมด = document.querySelectorAll("li");
รายการทั้งหมด.forEach((li) => {
  li.addEventListener("click", () => {
    console.log("คลิกรายการ");
  });
});
// 100 รายการ = 100 event listeners! 😱

// ✅ ดี: ใช้ Event Delegation
const กล่องใส่รายการ = document.querySelector("ul");

กล่องใส่รายการ.addEventListener("click", (event) => {
  // ตรวจสอบว่าคลิกที่ li
  if (event.target.tagName === "LI") {
    console.log("คลิกรายการ:", event.target.textContent);
  }
});
// 1 event listener จัดการทุกรายการ! 🎆

🎮 ลองทำดู: แบบฝึกหัด

📝 Exercise 1: สร้าง Calculator Function

javascript
// 🎯 จงสร้าง function คำนวณภาษี VAT 7%
// ที่รับราคาสินค้าและคืนค่าราคารวม VAT

function calculateVAT(price) {
  // เขียนโค้ดที่นี่
}

// ทดสอบ:
console.log(calculateVAT(100)); // ควรได้ 107
console.log(calculateVAT(500)); // ควรได้ 535
console.log(calculateVAT(1000)); // ควรได้ 1070
💡 เฉลย
javascript
function calculateVAT(price) {
  const vat = price * 0.07;
  return price + vat;
}

// หรือเขียนสั้นๆ
const calculateVAT = (price) => price * 1.07;

🔐 Exercise 2: Closure Counter

javascript
// 🎯 สร้าง counter ที่มี methods: increment, decrement, getValue
// โดยที่ค่า count ต้องเป็น private (เข้าถึงจากข้างนอกไม่ได้)

function createCounter(initialValue = 0) {
  // เขียนโค้ดที่นี่
}

// ทดสอบ:
const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.decrement(); // 11
console.log(counter.getValue()); // 11
console.log(counter.count); // undefined (private!)
💡 เฉลย
javascript
function createCounter(initialValue = 0) {
  let count = initialValue; // private variable

  return {
    increment() {
      count++;
      return count;
    },
    decrement() {
      count--;
      return count;
    },
    getValue() {
      return count;
    },
  };
}

⏱️ Exercise 3: Debounce Search

javascript
// 🎯 สร้าง debounced search function
// ที่จะค้นหาเมื่อผู้ใช้หยุดพิมพ์แล้ว 500ms

function createDebouncedSearch(searchFunction, delay = 500) {
  // เขียนโค้ดที่นี่
}

// ทดสอบ:
const search = createDebouncedSearch((query) => {
  console.log("Searching for:", query);
}, 500);

search("j"); // ไม่ค้นหา
search("ja"); // ไม่ค้นหา
search("jav"); // ไม่ค้นหา
search("java"); // ไม่ค้นหา
// รอ 500ms
// "Searching for: java" (ค้นหาครั้งเดียว!)
💡 เฉลย
javascript
function createDebouncedSearch(searchFunction, delay = 500) {
  let timeoutId;

  return function (query) {
    clearTimeout(timeoutId);

    timeoutId = setTimeout(() => {
      searchFunction(query);
    }, delay);
  };
}

🎉 Outcome: สิ่งที่คุณทำได้แล้ว

ยินดีด้วย! 🎊 หลังจบบทนี้ คุณสามารถ:

🔧 ทักษะการสร้าง Functions

  • เข้าใจความต่างของ 3 วิธีเขียน Function - Declaration (hoisted), Expression (flexible), Arrow (short & this inheritance)
  • จัดการ Parameters แบบมืออาชีพ - default values, rest parameters, destructuring
  • เข้าใจ this binding - รู้ว่า this ชี้ไปไหน และแก้ปัญหาด้วย arrow functions หรือ bind()

🏗️ ทักษะการจัดการ Scope

  • เข้าใจ Scope และ Hoisting - รู้ว่าตัวแปรมองเห็นกันยังไง JavaScript ยกอะไรขึ้นก่อน
  • ใช้ Closures สร้าง Private State - ซ่อนข้อมูลและสร้าง factory functions
  • เข้าใจ Scope Chain - รู้ว่า JavaScript หาตัวแปรยังไง ทำไมบางตัวเจอบางตัวไม่เจอ

🚀 ทักษะขั้นสูง

  • เขียน Higher-Order Functions - function ที่รับหรือ return function ใช้ในทุก modern framework
  • ใช้ Callback Pattern - ส่ง function ไปทำงานให้ foundation ของ async programming
  • Apply Real Patterns - debounce (รอให้หยุดก่อน), throttle (จำกัดความถี่) ใช้จริงในทุกเว็บ

🐛 ทักษะ Debug และ Optimize

  • Debug Functions และ Scope - ใช้ DevTools, console.trace(), breakpoints หา bug ได้เร็วขึ้น
  • เขียน Clean Functions - single responsibility, pure functions, avoid side effects
  • Optimize Performance - memoization, avoid recreating functions, smart event handling

🚀 ที่สำคัญที่สุด

  • มีพื้นฐานแข็งแรงสำหรับทุก Framework - React hooks, Vue composition, Angular services ทั้งหมดคือ functions
  • เขียนโค้ดที่ maintain ได้ - แยก logic เป็น functions เล็กๆ ทดสอบง่าย แก้ไขง่าย

💡 Key Takeaways - บทเรียนสำคัญ

🎯 สิ่งที่ต้องจำ

  1. Function Declaration hoisted, Expression/Arrow ไม่ hoisted - Declaration เรียกก่อนประกาศได้ แต่อีก 2 แบบต้องประกาศก่อน
  2. Arrow functions ไม่มี this ของตัวเอง - inherit จาก parent scope เหมาะกับ callbacks และ event handlers
  3. const/let = block scope, var = function scope - ลืม var ไปได้เลย ใช้แต่ const/let
  4. Closures จำ environment ได้ - function ที่เข้าถึงตัวแปรจาก outer scope ใช้สร้าง private state

🛠️ เคล็ดลับการใช้งาน

  1. Higher-order functions คือพื้นฐาน - map, filter, reduce, React hooks ทั้งหมดคือ higher-order functions
  2. Debounce รอให้หยุดก่อน, Throttle จำกัดความถี่ - debounce ใช้กับ search, throttle ใช้กับ scroll
  3. Pure functions เขียนง่าย test ง่าย - รับ input เดียวกัน ได้ output เดียวกัน ไม่มี side effects

🌟 Mindset ที่ถูกต้อง

  1. Functions คือ building blocks - แยกโค้ดเป็น functions เล็กๆ แต่ละอันทำงานเดียว
  2. Scope ช่วยป้องกัน bugs - ใช้ block scope จำกัดขอบเขตตัวแปร ป้องกันการเขียนทับโดยไม่ตั้งใจ
  3. Debug ด้วย DevTools ไม่ใช่ console.log - ใช้ breakpoints, call stack, scope inspector จะเห็นภาพรวมดีกว่า

🎯 Next Step - ก้าวต่อไป

📚 บทหน้า: Objects & Arrays

ในบทหน้า เราจะเรียนรู้การทำงานกับข้อมูลจริง:

🔹 Objects - ข้อมูลแบบมีโครงสร้าง

  • Object methods และ this context
  • Prototypes และ inheritance
  • Object destructuring และ shortcuts

🔹 Arrays - จัดการข้อมูลหลายตัว

  • Array methods ขั้นสูง (map, filter, reduce)
  • Chaining methods เขียนโค้ดสายเดียว
  • Array destructuring และ spread operator

🔹 Modern Patterns

  • Immutability - ทำไมต้องไม่แก้ข้อมูลเดิม
  • Functional programming กับ arrays
  • Real-world: data transformation, filtering, aggregation

🚀 พร้อมแล้วใช่ไหม?

คุณมี functions และ scope แข็งแรงแล้ว ต่อไปเรียนการจัดการข้อมูล!