⚙️ คู่มือการเขียน JavaScript

บทนำ JavaScript

JavaScript เป็นภาษาโปรแกรมมิ่งที่ทำงานบนเบราว์เซอร์ (และบน servers ด้วย Node.js) ใช้สำหรับสร้าง interactivity, การประมวลผลข้อมูล, validation forms, และการสร้าง web applications ที่ซับซ้อน รวมกับ HTML (โครงสร้าง) และ CSS (ออกแบบ) ทำให้เว็บไซต์เต็มไปด้วยชีวิต

JavaScript คืออะไร

ความหมาย

ใช้สำหรับ

วิธีใช้ JavaScript

1. Inline JavaScript

<button onclick="alert('Hello!')">Click me</button>

2. Internal JavaScript

<head>
  <script>
    console.log("Hello from JavaScript");
  </script>
</head>

3. External JavaScript (แนะนำ)

<script src="script.js" defer></script>
💡 ข้อมูล: ใส่ <script> ที่ท้ายของ <body> เพื่อให้เร็วขึ้น

Data Types

JavaScript มี 2 หมวดหมู่หลักของประเภทข้อมูล: Primitive Types (ข้อมูลพื้นฐาน) และ Reference Types (ข้อมูลอ้างอิง) แต่ละประเภทมีคุณสมบัติและการใช้งานที่แตกต่างกัน

1. Primitive Types (ข้อมูลพื้นฐาน)

Primitive types คือ ข้อมูลพื้นฐานที่เก็บค่าโดยตรง เมื่อเปรียบเทียบจะเปรียบเทียบค่า:

String - ข้อความ

// String - ข้อความใดๆ
let name = "John";
let city = 'Bangkok';
let message = `Hello, ${name}`; // Template literals (backticks)

// Template literals ช่วยให้งานง่าย
const greeting = `Hi, I'm ${name} from ${city}`;

// String methods
name.length;           // 4
name.toUpperCase();    // "JOHN"
name.toLowerCase();    // "john"
name.includes("oh");   // true
name.slice(0, 2);      // "Jo"

Number - ตัวเลข

// Number - ตัวเลขทั้ง integer และ decimal
let age = 25;           // Integer
let price = 99.99;      // Decimal (Float)
let negative = -50;     // Negative
let infinity = Infinity; // Special value

// Math operations
age + 5;               // 30
price * 2;             // 199.98
Math.round(99.5);      // 100
Math.max(10, 20, 5);   // 20
Math.random();         // 0-1 random number

Boolean - จริง/เท็จ

// Boolean - true หรือ false เท่านั้น
let isActive = true;
let isDeleted = false;

// ใช้กับ conditions
if (isActive) {
  console.log("User is active");
}

// Comparison ให้ boolean
10 > 5;        // true
"hello" === "hello"; // true
age < 18;      // false

Null & Undefined - ค่าว่าง

// Null - ค่าว่างที่ intentional (ตั้งใจ)
let x = null;  // assigned null

// Undefined - ค่าว่างอัตโนมัติ (ไม่ได้ assign)
let y;         // undefined
let z = undefined; // explicit undefined

// Difference
const empty = null;    // intentionally empty
const notAssigned = undefined; // not yet assigned

// Check for null/undefined
if (x === null) { }    // Check null
if (y === undefined) { } // Check undefined

2. Reference Types (ข้อมูลอ้างอิง)

Reference types คือ ข้อมูลที่เก็บเป็นการอ้างอิง เมื่อเปรียบเทียบจะเปรียบเทียบ reference ไม่ใช่ค่า:

Object - วัตถุข้อมูล

// Object - collection ของ key-value pairs
const person = {
  name: "John",
  age: 25,
  city: "Bangkok",
  isActive: true,
  
  // Method ในส่วนของ object
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

// Access properties
person.name;          // "John"
person["age"];        // 25

// Modify properties
person.age = 26;
person.email = "john@example.com"; // Add new property

// Object methods
Object.keys(person);     // ["name", "age", "city", ...]
Object.values(person);   // ["John", 26, "Bangkok", ...]
Object.entries(person);  // [["name", "John"], ["age", 26], ...]

Array - ลิสต์ข้อมูล

// Array - collection ของข้อมูล (ต่อเนื่องกัน)
let fruits = ["Apple", "Banana", "Orange"];
let numbers = [1, 2, 3, 4, 5];
let mixed = ["John", 25, true, null]; // Can mix types

// Access elements (0-indexed)
fruits[0];      // "Apple"
fruits[1];      // "Banana"
fruits.length;  // 3

// Array methods
fruits.push("Mango");         // Add to end
fruits.pop();                 // Remove from end
fruits.shift();               // Remove from start
fruits.unshift("Grape");      // Add to start
fruits.includes("Apple");     // true
fruits.indexOf("Banana");     // 1

Function - ฟังก์ชัน

// Function - โค้ดที่นำมาใช้ใหม่ได้
const add = (a, b) => a + b;          // Arrow function
const multiply = (a, b) => a * b;     // Arrow function

// Function declaration
function subtract(a, b) {
  return a - b;
}

// Call/invoke function
add(5, 3);         // 8
multiply(4, 2);    // 8
subtract(10, 3);   // 7

📊 Primitive vs Reference Comparison

ลักษณะ Primitive Types Reference Types
ประเภท String, Number, Boolean, Null, Undefined Object, Array, Function
การเก็บข้อมูล เก็บค่าโดยตรง เก็บ reference (address)
การเปรียบเทียบ เปรียบเทียบค่า เปรียบเทียบ reference ไม่ใช่ค่า
Example 5 === 5 → true {} === {} → false (ต่าง reference)
Assignment Copy ค่า Copy reference ไม่ใช่ค่า

Variables - ตัวแปร

ตัวแปร คือ ที่เก็บข้อมูล ใน JavaScript มี 3 วิธีสำหรับประกาศตัวแปร: const, let, และ var แต่ละวิธีมีข้อดีข้อเสียต่างกัน

1. const (แนะนำ - ใช้ที่สุด)

const ใช้สำหรับค่าคงที่ (constant) ที่ไม่เปลี่ยนแปลง ไม่สามารถ reassign ได้ แต่สามารถแก้ไขคุณสมบัติ (ถ้า object/array):

// Declare constant
const PI = 3.14159;
const MAX_USERS = 100;
const APP_NAME = "MyApp";

// Cannot reassign
// PI = 3.14;  // ❌ Error: Assignment to constant variable

// But can modify properties (if object/array)
const person = { name: "John" };
person.name = "Jane"; // ✅ OK: modifying property
person.age = 25;      // ✅ OK: adding property

// Cannot reassign the variable itself
// person = {};  // ❌ Error

// Block scope
{
  const x = 10;
  console.log(x); // 10
}
// console.log(x); // ❌ Error: not defined

2. let (ใช้เมื่อต้องการ reassign)

let ใช้สำหรับตัวแปรที่จะเปลี่ยนแปลง สามารถ reassign ได้ แต่ไม่สามารถ redeclare ได้ มี block scope:

// Declare variable with let
let x = 10;
x = 30;  // ✅ OK: can reassign
x = 50;  // ✅ OK: can reassign again

// Block scope
if (true) {
  let message = "Inside block";
  console.log(message); // ✅ Works
}
// console.log(message); // ❌ Error: not defined

// Loop counter (best practice)
for (let i = 0; i < 5; i++) {
  console.log(i);
}
// console.log(i); // ❌ Error: i is not defined outside loop

// Cannot redeclare
let y = 10;
// let y = 20; // ❌ Error: Identifier 'y' has already been declared

3. var (เลิกใช้แล้ว - หลีกเลี่ยง)

var ใช้ในยุก JavaScript เก่า แต่ไม่แนะนำให้ใช้ในโค้ดใหม่ เพราะมี quirks และ confusing scoping rules:

// var - old way (avoid in modern code)
var old = 5;  // Function scope, not block scope
old = 10; // ✅ Can reassign

// var ไม่มี block scope (problematic!)
if (true) {
  var x = 20;
}
console.log(x); // 20 (leaked out of if block) - ❌ Unexpected!

// ❌ ไม่ดี - unpredictable behavior
var age = 25;
var age = 30; // ✅ Can redeclare (confusing! shouldn't allow)

// ✅ ดี - use const/let instead
const name = "John";
// const name = "Jane"; // ❌ Error: cannot redeclare

📊 const vs let vs var Comparison

ลักษณะ const let var
Reassign ❌ ไม่ได้ ✅ ได้ ✅ ได้
Redeclare ❌ ไม่ได้ ❌ ไม่ได้ ✅ ได้ (confusing)
Scope Block scope Block scope Function scope
Hoisting Temporal Dead Zone Temporal Dead Zone Hoisted (undefined)
ใช้เมื่อ ค่าคงที่ (default) ตัวแปรที่เปลี่ยนแปลง ❌ หลีกเลี่ยง
✅ Best Practice (Modern JavaScript):
  • ใช้ const โดยค่าเริ่มต้น - ป้องกันการเปลี่ยนแปลงโดยไม่ตั้งใจ
  • ใช้ let เมื่อต้องการ reassign - สำหรับตัวแปรที่เปลี่ยนแปลง
  • หลีกเลี่ยง var ในโค้ดใหม่ - มี confusing behaviors

Operators - ตัวดำเนินการ

Operators คือ สัญลักษณ์ที่ใช้สำหรับดำเนินการกับข้อมูล เช่น การบวก การเปรียบเทียบ การตรรกะ เป็นต้น

1. Arithmetic Operators (ตัวดำเนินการทางคณิตศาสตร์)

ใช้สำหรับการคำนวณทางคณิตศาสตร์:

let a = 10, b = 3;

a + b;   // 13 (addition)
a - b;   // 7 (subtraction)
a * b;   // 30 (multiplication)
a / b;   // 3.33 (division)
a % b;   // 1 (modulo - remainder)
a ** b;  // 1000 (exponent - power)

// Increment/Decrement
let x = 5;
x++;     // 6 (increment by 1)
x--;     // 5 (decrement by 1)
++x;     // Pre-increment
x++;     // Post-increment (difference in expressions)

2. Comparison Operators (ตัวดำเนินการเปรียบเทียบ)

ใช้สำหรับเปรียบเทียบค่า ผลลัพธ์เป็น boolean (true/false):

// Equality
5 === "5";   // false (strict equality - type matters)
5 == "5";    // true (loose equality - type coercion)
5 !== "5";   // true (strict inequality)
5 != "5";    // false (loose inequality)

// Greater/Less than
10 > 5;      // true (greater than)
10 >= 10;    // true (greater than or equal)
5 < 10;      // true (less than)
5 <= 5;      // true (less than or equal)

// Best practice: always use === instead of ==
// === checks both value AND type (safer)

3. Logical Operators (ตัวดำเนินการตรรกะ)

ใช้สำหรับรวมหลายเงื่อนไขเข้าด้วยกัน:

// AND operator (&&) - true ถ้าทั้ง 2 เป็น true
true && false;     // false
true && true;      // true
(10 > 5) && (20 > 15); // true

// OR operator (||) - true ถ้า 1 ใน 2 เป็น true
true || false;     // true
false || false;    // false
(10 > 5) || (20 < 15); // true

// NOT operator (!) - กลับค่า
!true;             // false
!false;            // true
!(10 < 5);         // true

// Short-circuit evaluation
const user = null;
user && user.name;      // null (doesn't check user.name)
user || "Guest";        // "Guest" (returns "Guest" if user is falsy)

4. Assignment Operators (ตัวดำเนินการการกำหนด)

let x = 10;     // Simple assignment

x += 5;         // x = x + 5 (15)
x -= 3;         // x = x - 3 (12)
x *= 2;         // x = x * 2 (24)
x /= 4;         // x = x / 4 (6)
x %= 2;         // x = x % 2 (0)
x **= 2;        // x = x ** 2 (0)

📊 Operators Comparison Table

=, +=, -=, *=, /=
ประเภท Operator ตัวอย่าง ผลลัพธ์
Arithmetic +, -, *, /, %, ** 10 + 3 13
Comparison ===, !==, >, <, >=, <= 10 > 5 true
Logical &&, ||, ! true && false false
Assignment x += 5 x = x + 5
⚠️ Common Mistakes:
  • ❌ ใช้ == แทน === → อาจสร้าง type coercion ที่ไม่คาดคิด
  • ❌ ลืม && หรือ || → เงื่อนไขจะไม่ทำงานตามที่ต้องการ
  • ❌ สับสนระหว่าง = (assignment) กับ === (comparison)

Control Flow - การไหลของโปรแกรม

Control flow คือ ลำดับการทำงานของโปรแกรม ใช้ if/else, switch, loops เพื่อควบคุมว่าโค้ดส่วนไหนควรทำงาน

1. if...else (เงื่อนไข)

ใช้สำหรับตัดสินใจว่าจะทำส่วนไหนของโค้ด:

// Simple if
if (age >= 18) {
  console.log("Adult");
}

// if...else
if (age >= 18) {
  console.log("Adult");
} else {
  console.log("Minor");
}

// if...else if...else (multiple conditions)
if (age < 13) {
  console.log("Child");
} else if (age < 18) {
  console.log("Teenager");
} else {
  console.log("Adult");
}

// Ternary operator (short form)
const status = age >= 18 ? "Adult" : "Minor";
console.log(status);

2. switch (หลายตัวเลือก)

ใช้เมื่อต้องตัดสินใจจากค่าเพียงค่าเดียว:

// Switch statement
switch (day) {
  case 1:
    console.log("Monday");
    break;  // Important: ต้องใช้ break หรือจะไป execute case ถัดไป
  case 2:
    console.log("Tuesday");
    break;
  case 3:
    console.log("Wednesday");
    break;
  default:
    console.log("Unknown day");
}

// Switch กับ multiple cases
const status = "active";
switch (status) {
  case "active":
  case "pending":
    console.log("User is active or pending");
    break;
  case "inactive":
    console.log("User is inactive");
    break;
  default:
    console.log("Unknown status");
}

3. Loops (วนซ้ำ)

ใช้สำหรับทำงานซ้ำๆ หลายครั้ง:

for Loop - วนซ้ำตามจำนวน

// Traditional for loop
for (let i = 0; i < 5; i++) {
  console.log(i);  // 0, 1, 2, 3, 4
}

// for loop กับ array
const fruits = ["Apple", "Banana", "Orange"];
for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

for...of Loop - วนซ้ำค่า (ES6+)

// for...of - วนซ้ำค่าของ array
const fruits = ["Apple", "Banana", "Orange"];
for (let fruit of fruits) {
  console.log(fruit);  // Apple, Banana, Orange
}

// for...of กับ string
const name = "John";
for (let char of name) {
  console.log(char);  // J, o, h, n
}

while Loop - วนซ้ำตามเงื่อนไข

// While loop - วนซ้ำจนกว่าเงื่อนไขเป็น false
let count = 0;
while (count < 5) {
  console.log(count);
  count++;
}

// do...while - ทำงานอย่างน้อย 1 ครั้ง
let x = 0;
do {
  console.log(x);
  x++;
} while (x < 5);

break และ continue

// break - ออกจาก loop
for (let i = 0; i < 10; i++) {
  if (i === 5) {
    break; // ออกจาก loop เมื่อ i = 5
  }
  console.log(i);  // 0, 1, 2, 3, 4
}

// continue - ข้ามไปรอบถัดไป
for (let i = 0; i < 5; i++) {
  if (i === 2) {
    continue; // ข้ามค่า 2
  }
  console.log(i);  // 0, 1, 3, 4
}

📊 Control Flow Comparison

ประเภท ใช้เมื่อ ตัวอย่าง
if...else เงื่อนไขหลายตัว age < 13, age < 18, else
switch ค่าเดียว หลายตัวเลือก day = 1, 2, 3, default
for วนซ้ำตามจำนวน i = 0 to 10
for...of วนซ้ำค่าในarray for (item of array)
while วนซ้ำตามเงื่อนไข while (x < 10)

Functions - ฟังก์ชัน

Function คือ โค้ดที่ทำงานเฉพาะเจาะจง และนำมาใช้ใหม่ได้หลายครั้ง ทำให้โค้ดเป็นระเบียบและป้องกันการทำซ้ำ

1. Function Declaration - ประกาศฟังก์ชัน

// Traditional function declaration
function add(a, b) {
  return a + b;
}

// Call the function
add(5, 3);     // 8
add(10, 20);   // 30

// Function with multiple statements
function greet(name) {
  const message = `Hello, ${name}`;
  console.log(message);
  return message;
}

greet("John");  // Hello, John

2. Arrow Function (ES6+) - ฟังก์ชันลูกศร

Arrow function เป็นวิธีเขียน function ที่กระชับและสั้นกว่า:

// Arrow function - short syntax
const add = (a, b) => a + b;
const square = (x) => x * x;
const greet = (name) => `Hello, ${name}`;

// Call them
add(5, 3);      // 8
square(5);      // 25
greet("John");  // Hello, John

// Arrow function กับ multiple statements
const calculateAge = (birthYear) => {
  const currentYear = new Date().getFullYear();
  return currentYear - birthYear;
};

calculateAge(1990);  // 34

// Without parameters
const random = () => Math.random();

// One parameter (parentheses optional)
const double = x => x * 2;
const double = (x) => x * 2; // Same thing

3. Parameters & Arguments - พารามิเตอร์และอาร์กิวเมนต์

// Default parameters
function greet(name = "Guest", age = 18) {
  console.log(`Hi ${name}, age ${age}`);
}

greet();              // Hi Guest, age 18
greet("John");        // Hi John, age 18
greet("John", 25);    // Hi John, age 25

// Rest parameters (...) - รับพารามิเตอร์หลายตัว
function sum(...numbers) {
  let total = 0;
  for (let num of numbers) {
    total += num;
  }
  return total;
}

sum(1, 2, 3);         // 6
sum(1, 2, 3, 4, 5);   // 15

// Rest parameter กับ spread operator
const arr = [1, 2, 3];
sum(...arr);          // 6

4. Closures - ความสัมพันธ์ระหว่างฟังก์ชัน

Closure คือ ฟังก์ชันภายในสามารถเข้าถึงตัวแปรของฟังก์ชันภายนอก:

// Closure example
function createCounter() {
  let count = 0; // Local variable
  
  return function () {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter());  // 1
console.log(counter());  // 2
console.log(counter());  // 3

// Each counter is independent
const counter2 = createCounter();
console.log(counter2());  // 1 (separate from counter)

// Practical closure example - makeGreeter
function makeGreeter(greeting) {
  return function (name) {
    console.log(`${greeting}, ${name}!`);
  };
}

const sayHello = makeGreeter("Hello");
const sayHi = makeGreeter("Hi");

sayHello("John");     // Hello, John!
sayHi("Jane");        // Hi, Jane!

5. Function Methods - ฟังก์ชันที่อยู่ใน Object

// Object with methods
const calculator = {
  value: 0,
  
  add(num) {
    this.value += num;
    return this;
  },
  
  subtract(num) {
    this.value -= num;
    return this;
  },
  
  getResult() {
    return this.value;
  }
};

// Method chaining
calculator.add(10).subtract(3).add(5);
console.log(calculator.getResult());  // 12

📊 Function Types Comparison

ประเภท ไวยากรณ์ ใช้เมื่อ ตัวอย่าง
Declaration function name() {} ฟังก์ชันปกติ function add(a, b) { return a + b; }
Arrow const name = () => {} ฟังก์ชันสั้น, callbacks const add = (a, b) => a + b;
Closure function() { return function() {} } ข้อมูล private, factory createCounter()
Method object: { method() {} } ฟังก์ชันใน object person.greet()
💡 Function Best Practices:
  • ✅ ใช้ meaningful function names
  • ✅ Keep functions small (single responsibility)
  • ✅ ใช้ const สำหรับ arrow functions
  • ✅ Return values instead of console.log

Objects & Arrays - วัตถุและลิสต์

Objects และ Arrays เป็นประเภท Reference ที่ใช้เก็บข้อมูลหลายตัว Objects เก็บ key-value pairs ส่วน Arrays เก็บลำดับของค่า

1. Objects - วัตถุข้อมูล

Objects ใช้สำหรับเก็บข้อมูลที่เกี่ยวข้องกันในรูป key-value pairs:

// Create object
const person = {
  name: "John",
  age: 25,
  email: "john@example.com",
  isActive: true,
  
  // Methods (functions ใน object)
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  },
  
  getInfo() {
    return `${this.name} is ${this.age} years old`;
  }
};

// Access properties
person.name;           // "John"
person["age"];         // 25

// Modify properties
person.age = 26;
person.email = "john.doe@example.com";

// Add new property
person.phone = "0881234567";

// Object methods
Object.keys(person);     // ["name", "age", "email", "isActive", ...]
Object.values(person);   // ["John", 26, "john.doe@example.com", true, ...]
Object.entries(person);  // [["name", "John"], ["age", 26], ...]

// Method chaining example
person.greet();          // Hello, I'm John
console.log(person.getInfo()); // John is 26 years old

2. Destructuring - แตกส่วน

Destructuring ให้สามารถแยกค่าจาก Objects หรือ Arrays ออกมาเป็นตัวแปรแต่ละตัว:

// Object destructuring - แตกส่วน object
const person = { name: "John", age: 25, city: "Bangkok" };

const { name, age } = person;
console.log(name); // "John"
console.log(age);  // 25

// Rename properties
const { name: fullName, age: userAge } = person;

// Default values
const { country = "Thailand" } = person;
console.log(country); // "Thailand" (default value)


// Array destructuring - แตกส่วน array
const colors = ["red", "green", "blue"];
const [first, second, third] = colors;
console.log(first);  // "red"
console.log(second); // "green"

// Skip elements
const [primary, , tertiary] = colors;
console.log(primary);  // "red"
console.log(tertiary); // "blue"

// Rest operator
const [a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(rest); // [3, 4, 5]

3. Array Methods - เมธอดของ Array

Array มีเมธอดมากมายที่ช่วยในการจัดการข้อมูล นี่คือเมธอดที่สำคัญที่สุด:

const numbers = [1, 2, 3, 4, 5];
const fruits = ["Apple", "Banana", "Orange"];

// map() - transform each element
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter() - keep only matching elements
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]

// reduce() - combine all elements into one
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15

// find() - return first matching element
const first = numbers.find(n => n > 3);
console.log(first); // 4

// some() - check if any element matches
const hasEven = numbers.some(n => n % 2 === 0);
console.log(hasEven); // true

// every() - check if all elements match
const allPositive = numbers.every(n => n > 0);
console.log(allPositive); // true

// includes() - check if contains value
console.log(fruits.includes("Banana")); // true

// sort() - sort array
const sorted = numbers.sort((a, b) => a - b);
console.log(sorted); // [1, 2, 3, 4, 5]

// reverse() - reverse array
const reversed = [...numbers].reverse();
console.log(reversed); // [5, 4, 3, 2, 1]

DOM Manipulation - การจัดการ DOM

DOM (Document Object Model) คือ โครงสร้างของ HTML page ใน JavaScript เราสามารถใช้ DOM API เพื่อเลือก, แก้ไข, เพิ่ม, ลบ elements ได้

1. Selecting Elements - เลือก Element

มีหลายวิธีในการเลือก elements จาก HTML:

// By ID - เลือกตามชื่อ id
const el = document.getElementById("myId");

// By class - เลือกตามชื่อ class
const els = document.getElementsByClassName("myClass");

// By tag - เลือกตามชื่อ tag
const paragraphs = document.getElementsByTagName("p");

// Query Selector (modern way - แนะนำ)
const first = document.querySelector(".myClass");  // ได้ element แรกเท่านั้น
const all = document.querySelectorAll(".myClass"); // ได้ทั้งหมด

// Advanced selectors
const elem = document.querySelector("div.active > p"); // ระบุได้ละเอียด
const items = document.querySelectorAll("li.item");    // หลาย elements

2. Modifying Content - แก้ไขเนื้อหา

เปลี่ยนข้อความหรือ HTML ของ element:

const el = document.querySelector("#myElement");

// Change text content (safe - ไม่รันตัว script)
el.textContent = "New text";

// Change HTML (ระวัง - ถ้ากำหนดจากนอก อาจมี XSS risk)
el.innerHTML = "<strong>Bold text</strong>";

// Get current content
console.log(el.textContent); // Get text only
console.log(el.innerHTML);   // Get HTML

// Append text/HTML
el.textContent += " additional text";
el.innerHTML += "<p>new paragraph</p>";

3. Modifying Attributes - แก้ไขแอตทริบิวต์

const img = document.querySelector("img");

// Get attribute
console.log(img.getAttribute("src"));

// Set attribute
img.setAttribute("src", "new-image.jpg");
img.setAttribute("alt", "New description");

// Direct property access
img.src = "another-image.jpg";
img.alt = "Another description";

// Remove attribute
img.removeAttribute("alt");

// Check if has attribute
if (img.hasAttribute("src")) {
  console.log("Image has src attribute");
}

4. Modifying Classes - แก้ไข Class

ใช้ classList เพื่อจัดการ CSS classes:

const el = document.querySelector(".box");

// Add class
el.classList.add("active");      // เพิ่ม class
el.classList.add("red", "large"); // เพิ่มหลาย class

// Remove class
el.classList.remove("inactive");

// Toggle class (เปลี่ยนสลับ)
el.classList.toggle("highlight");

// Check if has class
if (el.classList.contains("active")) {
  console.log("Element is active");
}

// Replace class
el.classList.replace("old-class", "new-class");

5. Modifying Styles - แก้ไขสไตล์

const el = document.querySelector(".box");

// Inline styles
el.style.backgroundColor = "red";
el.style.width = "300px";
el.style.padding = "20px";

// Get computed style (current CSS)
const bgColor = window.getComputedStyle(el).backgroundColor;

// Multiple styles at once (better approach)
Object.assign(el.style, {
  backgroundColor: "blue",
  color: "white",
  padding: "15px",
  fontSize: "16px"
});

6. Creating & Removing Elements - สร้างและลบ Element

// Create new element
const newDiv = document.createElement("div");
newDiv.textContent = "Hello!";
newDiv.classList.add("greeting");

// Add to page
document.body.appendChild(newDiv);           // เพิ่มต่อท้าย
const container = document.querySelector("#container");
container.appendChild(newDiv);

// Add before element
const referenceEl = document.querySelector(".reference");
referenceEl.parentElement.insertBefore(newDiv, referenceEl);

// Remove element
newDiv.remove();
// หรือ
newDiv.parentElement.removeChild(newDiv);

// Clone element
const clone = newDiv.cloneNode(true);  // true = clone ลูกด้วย
container.appendChild(clone);

Events - เหตุการณ์

Events คือ สิ่งที่เกิดขึ้นบน page เช่น การคลิก, พิมพ์, scroll เป็นต้น เราสามารถ "listen" เหตุการณ์เหล่านั้นและทำสิ่งใดสิ่งหนึ่งเมื่อเหตุการณ์เกิดขึ้น

1. Event Listeners - ฟังการเกิดเหตุการณ์

ใช้ addEventListener() เพื่อฟังเหตุการณ์:

const button = document.querySelector("button");

// Listen to click event
button.addEventListener("click", () => {
  console.log("Button clicked!");
});

// Listen to input event
const input = document.querySelector("input");
input.addEventListener("input", (event) => {
  console.log("User typed:", event.target.value);
});

// Listen to keyboard event
document.addEventListener("keydown", (event) => {
  console.log("Key pressed:", event.key);
  console.log("Key code:", event.code);
});

// Access event object
button.addEventListener("click", (event) => {
  console.log(event.target);      // element ที่ triggered event
  console.log(event.type);        // ประเภท event
  console.log(event.timeStamp);   // เวลา event เกิด
});

2. Common Events - เหตุการณ์ทั่วไป

ประเภท Event เมื่อไร ตัวอย่าง
Mouse click, dblclick, mouseover, mouseout, mouseenter, mouseleave ใช้เมื่อผู้ใช้ใช้เมาส์ button.addEventListener("click", ...)
Keyboard keydown, keyup, keypress ใช้เมื่อผู้ใช้กด keyboard input.addEventListener("keydown", ...)
Form submit, change, input, focus, blur ใช้เมื่อ form เปลี่ยนแปลง form.addEventListener("submit", ...)
Document/Window load, unload, scroll, resize ใช้เมื่อ page เปลี่ยนแปลง window.addEventListener("scroll", ...)
Custom กำหนดเองได้ สร้าง event เอง element.dispatchEvent(new Event("custom"))

3. Practical Event Examples - ตัวอย่างจริง

// Example 1: Form submission
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
  event.preventDefault(); // ป้องกัน default behavior (submit)
  
  const name = document.querySelector("input[name='name']").value;
  const email = document.querySelector("input[name='email']").value;
  
  console.log("Name:", name);
  console.log("Email:", email);
  // Send to server
});


// Example 2: Input validation (real-time)
const passwordInput = document.querySelector("input[type='password']");
const strength = document.querySelector(".strength");

passwordInput.addEventListener("input", (event) => {
  const value = event.target.value;
  
  if (value.length < 6) {
    strength.textContent = "Weak";
    strength.style.color = "red";
  } else if (value.length < 10) {
    strength.textContent = "Medium";
    strength.style.color = "orange";
  } else {
    strength.textContent = "Strong";
    strength.style.color = "green";
  }
});


// Example 3: Event delegation (listen to many elements)
const list = document.querySelector(".item-list");
list.addEventListener("click", (event) => {
  // ตรวจสอบว่าคลิกที่ element ไหน
  if (event.target.classList.contains("item-btn")) {
    const item = event.target.closest(".item");
    console.log("Clicked item:", item.textContent);
  }
});


// Example 4: Remove event listener
function handleClick() {
  console.log("Clicked!");
}

button.addEventListener("click", handleClick);

// Remove listener
button.removeEventListener("click", handleClick);

4. Event Properties - คุณสมบัติของ Event

document.addEventListener("click", (event) => {
  // Basic info
  console.log(event.type);           // "click"
  console.log(event.target);         // element ที่คลิก
  console.log(event.currentTarget);  // element ที่มี listener
  
  // Mouse info
  console.log(event.clientX);        // X position relative to window
  console.log(event.clientY);        // Y position relative to window
  console.log(event.pageX);          // X position relative to page
  console.log(event.pageY);          // Y position relative to page
  
  // Keyboard info (for keyboard events)
  console.log(event.key);            // ตัวอักษรที่กด
  console.log(event.code);           // รหัส key
  console.log(event.shiftKey);       // true ถ้ากด Shift
  console.log(event.ctrlKey);        // true ถ้ากด Ctrl
  console.log(event.altKey);         // true ถ้ากด Alt
  
  // Prevention
  event.preventDefault();            // ป้องกัน default behavior
  event.stopPropagation();           // ป้องกัน event bubbling
});

Asynchronous JavaScript

Asynchronous หมายถึง โค้ดที่ไม่ต้องรอให้เสร็จแล้วค่อยทำสิ่งต่อไป เช่น การดึงข้อมูลจาก server อาจใช้เวลา เราไม่อยากให้ page รอ ดังนั้นจึงใช้ async code

1. Callbacks - ฟังก์ชันที่ส่งผ่าน

วิธีเก่า: ส่งฟังก์ชันเป็น parameter เพื่อเรียกใหม่เมื่อเสร็จ:

// Callback - old way (callback hell)
function fetchData(callback) {
  setTimeout(() => {
    const data = { name: "John", age: 25 };
    callback(data); // เรียก callback function
  }, 1000);
}

fetchData((result) => {
  console.log("Data received:", result);
});

// Nested callbacks (callback hell)
fetchData((user) => {
  fetchUserPosts(user.id, (posts) => {
    fetchPostComments(posts[0].id, (comments) => {
      console.log(comments);
      // ยากเข้าใจและ maintain
    });
  });
});

2. Promises - สัญญา

Promise ทำให้ async code อ่านง่ายขึ้น แต่ยังดีกว่า callbacks:

// Create a promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    
    if (success) {
      resolve("Success!"); // ส่งผลลัพธ์กลับ
    } else {
      reject("Error!"); // ส่ง error กลับ
    }
  }, 1000);
});

// Use the promise
promise
  .then(result => {
    console.log("Result:", result);
  })
  .catch(error => {
    console.log("Error:", error);
  })
  .finally(() => {
    console.log("Done!");
  });

// Promise chaining
fetch("https://api.example.com/users")
  .then(response => response.json())
  .then(data => {
    console.log("Users:", data);
    return fetch(`https://api.example.com/users/${data[0].id}`);
  })
  .then(response => response.json())
  .then(user => console.log("First user:", user))
  .catch(error => console.log("Error:", error));

3. Async/Await - วิธีใหม่ (แนะนำ)

Async/Await ทำให้ async code อ่านเหมือน synchronous code:

// Define async function
async function fetchUserData() {
  try {
    // await บอก JavaScript ให้รอให้เสร็จ
    const response = await fetch("https://api.example.com/users");
    const data = await response.json();
    
    console.log("Users:", data);
    return data;
    
  } catch (error) {
    console.log("Error:", error);
  } finally {
    console.log("Request completed!");
  }
}

// Call async function
fetchUserData();

// Async with multiple requests
async function getUserWithPosts(userId) {
  try {
    // Fetch user
    const userResponse = await fetch(`https://api.example.com/users/${userId}`);
    const user = await userResponse.json();
    
    // Fetch posts
    const postsResponse = await fetch(`https://api.example.com/posts?userId=${userId}`);
    const posts = await postsResponse.json();
    
    return { user, posts };
    
  } catch (error) {
    console.log("Error:", error);
  }
}


// Parallel requests (faster)
async function fetchMultiple() {
  try {
    // Run requests in parallel using Promise.all()
    const [users, posts, comments] = await Promise.all([
      fetch("https://api.example.com/users").then(r => r.json()),
      fetch("https://api.example.com/posts").then(r => r.json()),
      fetch("https://api.example.com/comments").then(r => r.json())
    ]);
    
    console.log("Users:", users);
    console.log("Posts:", posts);
    console.log("Comments:", comments);
    
  } catch (error) {
    console.log("Error:", error);
  }
}

4. Fetch API - ดึงข้อมูล

Fetch API ใช้สำหรับส่ง HTTP requests (GET, POST, PUT, DELETE):

// GET request (default)
fetch("https://api.example.com/users")
  .then(response => response.json())
  .then(data => console.log("Users:", data))
  .catch(error => console.log("Error:", error));

// GET with async/await
async function getUsers() {
  try {
    const response = await fetch("https://api.example.com/users");
    const data = await response.json();
    console.log("Users:", data);
  } catch (error) {
    console.log("Error:", error);
  }
}


// POST request
async function createUser(userData) {
  try {
    const response = await fetch("https://api.example.com/users", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(userData) // Convert object to JSON string
    });
    
    const result = await response.json();
    console.log("Created:", result);
  } catch (error) {
    console.log("Error:", error);
  }
}

createUser({ name: "John", email: "john@example.com" });


// PUT request (update)
async function updateUser(userId, updatedData) {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(updatedData)
  });
  return response.json();
}


// DELETE request
async function deleteUser(userId) {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    method: "DELETE"
  });
  return response.json();
}

Error Handling - การจัดการข้อผิดพลาด

Error Handling ช่วยให้โปรแกรมไม่ขัดข้อง เมื่อมีปัญหา ให้จัดการได้อย่างถูกต้อง:

1. Try/Catch/Finally

ใช้สำหรับจับและจัดการข้อผิดพลาด:

// Basic try/catch
try {
  // โค้ดที่อาจเกิด error
  const result = JSON.parse("invalid json");
  console.log(result);
} catch (error) {
  // จับและจัดการ error
  console.log("Caught error:", error.message);
} finally {
  // รันเสมอ ไม่ว่าจะมี error หรือไม่
  console.log("Done!");
}

// With finally
try {
  const data = someUndefinedFunction();
} catch (error) {
  console.log("Error occurred");
} finally {
  console.log("Cleanup complete");
}

// Multiple catch blocks
try {
  const file = readFile("data.txt");
} catch (error) {
  if (error instanceof FileNotFoundError) {
    console.log("File not found");
  } else if (error instanceof PermissionError) {
    console.log("Permission denied");
  } else {
    console.log("Unknown error:", error);
  }
}

2. Error Types - ประเภทของ Error

JavaScript มี error types ต่างๆ แต่ละประเภทแสดงปัญหาคนละแบบ:

Error Type ความหมาย ตัวอย่าง
SyntaxError โค้ดผิดไวยากรณ์ let x = 5 (missing semicolon)
ReferenceError ตัวแปรไม่มีอยู่ console.log(undefinedVar);
TypeError ใช้ค่าผิด type let x = 5; x.toUpperCase();
RangeError ค่านอกขอบเขต new Array(-1);
Error Generic error throw new Error("Custom error")

3. Throwing Errors - โยน Error เอง

บางครั้งเราต้อง throw error เองเมื่อเงื่อนไขไม่ถูก:

// Throw custom error
function divide(a, b) {
  if (b === 0) {
    throw new Error("Cannot divide by zero!");
  }
  return a / b;
}

try {
  const result = divide(10, 0);
} catch (error) {
  console.log("Error:", error.message);
}

// Throw specific error type
function validateAge(age) {
  if (age < 0) {
    throw new RangeError("Age cannot be negative");
  }
  if (typeof age !== "number") {
    throw new TypeError("Age must be a number");
  }
  return true;
}

try {
  validateAge(-5);
} catch (error) {
  if (error instanceof RangeError) {
    console.log("Range error:", error.message);
  }
}

// Error with details
class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

try {
  throw new ValidationError("Invalid email", "email");
} catch (error) {
  console.log(error.message); // Invalid email
  console.log(error.field); // email
}

4. Best Practices - วิธีที่ถูก

✅ ทำให้ error handling มีประสิทธิภาพและง่ายต่อ maintain:

// ✅ ดี: ให้ message ที่ชัดเจน
try {
  const user = await fetchUser(userId);
} catch (error) {
  console.error("Failed to fetch user:", error.message);
  // Show error to user หรือ retry
}

// ❌ ไม่ดี: silent fail
try {
  const user = await fetchUser(userId);
} catch (error) {
  // ปล่อยไป ไม่ทำอะไร
}

// ✅ ดี: Handle different error types
async function processData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return response.json();
  } catch (error) {
    if (error instanceof TypeError) {
      console.error("Network error:", error.message);
    } else if (error instanceof SyntaxError) {
      console.error("Invalid JSON:", error.message);
    } else {
      console.error("Unknown error:", error.message);
    }
    throw error; // Re-throw to parent
  }
}

// ✅ ดี: Finally for cleanup
function readFile(path) {
  let file = null;
  try {
    file = openFile(path);
    return parseData(file);
  } catch (error) {
    console.error("Error reading file:", error);
  } finally {
    // รันเสมอ ให้ปิด file
    if (file) {
      file.close();
    }
  }
}

ES6+ Features - ฟีเจอร์ใหม่

ES6 (ECMAScript 2015) และ ES6+ (2016 ขึ้นไป) นำเอาฟีเจอร์ใหม่ๆ มาให้ JavaScript อ่านง่ายและมีประสิทธิภาพขึ้น:

1. Template Literals - String ด้วย backticks

ใช้ backticks (`) แทน quotes ปกติ สามารถ embed variables ได้:

// Old way (concatenation)
const name = "John";
const age = 25;
const message = "Hello, " + name + "! You are " + age + " years old.";

// New way (template literals)
const messageNew = `Hello, ${name}! You are ${age} years old.`;
console.log(messageNew);

// Multi-line string
const bio = `
  Name: John Doe
  Age: 25
  Occupation: Developer
`;
console.log(bio);

// Expressions in template literals
const x = 10;
const y = 20;
console.log(`${x} + ${y} = ${x + y}`);

// Function calls in template literals
function getFullName(first, last) {
  return `${first} ${last}`;
}

const greeting = `Welcome, ${getFullName("John", "Doe")}!`;
console.log(greeting);

2. Arrow Functions - ลัดขนัด

วิธีเขียน function แบบสั้นและสะดวก:

// Old way (regular function)
const add = function(a, b) {
  return a + b;
};

// Arrow function (basic)
const addArrow = (a, b) => {
  return a + b;
};

// Arrow function (single line)
const addShort = (a, b) => a + b;

// Arrow function (single parameter)
const square = x => x * x;

// Arrow function (no parameters)
const greet = () => "Hello!";

// Difference: 'this' keyword
const person = {
  name: "John",
  sayHello: function() {
    console.log(`Hello, I'm ${this.name}`); // ✅ this works
  },
  sayHelloArrow: () => {
    console.log(`Hello, I'm ${this.name}`); // ❌ this doesn't work (global this)
  }
};

person.sayHello(); // "Hello, I'm John"
person.sayHelloArrow(); // "Hello, I'm undefined"

3. Classes - Object-Oriented Programming

ใช้ class เพื่อ organize code และ reuse logic:

// Define a class
class Animal {
  // Constructor - รันเมื่อ new Animal()
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  // Method
  speak() {
    console.log(`${this.name} makes a sound`);
  }
  
  // Static method (belongs to class, not instance)
  static info() {
    console.log("This is an Animal class");
  }
}

// Create instance
const dog = new Animal("Buddy", 5);
dog.speak(); // "Buddy makes a sound"
Animal.info(); // "This is an Animal class"

// Inheritance (extends)
class Dog extends Animal {
  constructor(name, age, breed) {
    super(name, age); // Call parent constructor
    this.breed = breed;
  }
  
  speak() {
    console.log(`${this.name} barks!`);
  }
  
  getInfo() {
    return `${this.name} is a ${this.breed}`;
  }
}

const myDog = new Dog("Max", 3, "Golden Retriever");
myDog.speak(); // "Max barks!"
console.log(myDog.getInfo()); // "Max is a Golden Retriever"

4. Destructuring - แยกค่า

วิธีสั้นๆ ดึงค่าจาก objects และ arrays:

// Object destructuring
const person = { 
  name: "John", 
  age: 25, 
  city: "Bangkok" 
};

// Old way
const name = person.name;
const age = person.age;

// New way
const { name, age } = person;
console.log(name); // "John"

// With default values
const { name, country = "Thailand" } = person;
console.log(country); // "Thailand" (uses default)

// Array destructuring
const colors = ["red", "green", "blue"];

// Old way
const first = colors[0];
const second = colors[1];

// New way
const [first, second, third] = colors;

// Skipping elements
const [primary, , secondary] = colors;
console.log(primary); // "red"
console.log(secondary); // "blue"

// Rest operator (...)
const [head, ...tail] = colors;
console.log(head); // "red"
console.log(tail); // ["green", "blue"]

5. Spread Operator - กระจาย

ใช้ ... เพื่อ copy หรือ merge arrays/objects:

// Spread with arrays
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

// Copy array
const original = [1, 2, 3];
const copy = [...original]; // [1, 2, 3] (separate array)

// Merge arrays
const array1 = [1, 2];
const array2 = [3, 4];
const merged = [...array1, ...array2]; // [1, 2, 3, 4]

// Spread with objects
const obj1 = { name: "John", age: 25 };
const obj2 = { ...obj1, city: "Bangkok" };
// { name: "John", age: 25, city: "Bangkok" }

// Override properties
const base = { color: "red", size: "large" };
const custom = { ...base, color: "blue" };
// { color: "blue", size: "large" }

// Rest parameter in function
function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

6. let และ const - ตัวแปรใหม่

var ยุค่า scope ใหญ่เกินไป ให้ใช้ let/const แทน:

// var - ❌ Avoid (function scoped)
function oldWay() {
  if (true) {
    var x = 5;
  }
  console.log(x); // 5 (accessible outside block)
}

// let - ✅ Block scoped
function newWay() {
  if (true) {
    let x = 5;
  }
  console.log(x); // ❌ ReferenceError (not accessible)
}

// const - ✅ Block scoped + can't reassign
const PI = 3.14159;
// PI = 3.14; // ❌ Error: reassignment not allowed

// But const object properties CAN change
const user = { name: "John", age: 25 };
user.age = 26; // ✅ Allowed
// user = {}; // ❌ Error: can't reassign whole object

7. Default Parameters - ค่า default

Set ค่าเริ่มต้นสำหรับ function parameters:

// Old way (check for undefined)
function greet(name) {
  name = name || "Guest";
  console.log(`Hello, ${name}`);
}

// New way (default parameter)
function greetNew(name = "Guest") {
  console.log(`Hello, ${name}`);
}

greetNew(); // "Hello, Guest"
greetNew("John"); // "Hello, John"

// Can use expressions as defaults
function createUser(name = "Anonymous", age = 18) {
  return { name, age };
}

console.log(createUser()); 
// { name: "Anonymous", age: 18 }

Best Practices - วิธีที่ถูกต้อง

Best Practices ช่วยให้โค้ดอ่านได้ง่าย บำรุงรักษาได้ง่าย และมีประสิทธิภาพ:

1. Meaningful Names - ใช้ชื่อที่มีความหมาย

ชื่อตัวแปร/function ต้องบอกว่า มันทำอะไร:

// ❌ Bad names (unclear)
let a = 25;
let fn = (x) => x * 2;
let d = new Date();
let calc = (p, r, t) => p * r * t;

// ✅ Good names (clear intent)
let userAge = 25;
let doubleNumber = (number) => number * 2;
let currentDate = new Date();
let calculateInterest = (principal, rate, time) => principal * rate * time;

// ✅ Good boolean names (predicate)
let isActive = true;
let hasPermission = false;
let canDelete = true;
let shouldRetry = false;

2. Keep Functions Small - ฟังก์ชั่นเล็ก

แต่ละ function ทำเพียงหนึ่งสิ่ง (Single Responsibility):

// ❌ Bad: Function does too much
function processUser(user) {
  // Validate
  if (!user.email) throw new Error("Email required");
  
  // Format
  user.name = user.name.toUpperCase();
  user.email = user.email.toLowerCase();
  
  // Save
  database.save(user);
  
  // Send email
  sendEmail(user.email, "Welcome!");
}

// ✅ Good: Separate concerns
function validateUser(user) {
  if (!user.email) throw new Error("Email required");
  if (!user.name) throw new Error("Name required");
  return true;
}

function formatUser(user) {
  return {
    ...user,
    name: user.name.toUpperCase(),
    email: user.email.toLowerCase()
  };
}

function saveUser(user) {
  return database.save(user);
}

// Main flow: clear and easy to understand
async function registerUser(userData) {
  validateUser(userData);
  const formatted = formatUser(userData);
  await saveUser(formatted);
  await sendEmail(formatted.email, "Welcome!");
}

3. Handle Errors - จัดการ Error อย่างถูกต้อง

ไม่ให้โปรแกรมขัดข้อง ต้องจัดการ error:

// ❌ Bad: Ignore error (silent fail)
function parseJSON(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch (error) {
    // Do nothing - program might break later
  }
}

// ✅ Good: Log and handle error
function parseJSON(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch (error) {
    console.error("Invalid JSON:", error.message);
    return null; // Return safe default
  }
}

// ✅ Better: Return result object
function parseJSONSafe(jsonString) {
  try {
    const data = JSON.parse(jsonString);
    return { success: true, data };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

// Usage
const result = parseJSONSafe(userInput);
if (result.success) {
  console.log("Data:", result.data);
} else {
  console.log("Error:", result.error);
}

4. Performance - ประสิทธิภาพ

เขียนโค้ดที่รวดเร็วและไม่เสียเวลา:

// ❌ Bad: Query DOM multiple times (slow)
for (let i = 0; i < 1000; i++) {
  document.getElementById("output").textContent += i;
}

// ✅ Good: Access DOM once (fast)
const output = document.getElementById("output");
let result = "";
for (let i = 0; i < 1000; i++) {
  result += i;
}
output.textContent = result;

// ❌ Bad: Nested loops O(n²)
const users = [{ id: 1 }, { id: 2 }, { id: 3 }];
const ids = [1, 2, 3, 4, 5];

for (let user of users) {
  for (let id of ids) {
    if (user.id === id) {
      console.log(user);
    }
  }
}

// ✅ Good: Use Set for O(n) lookup
const users = [{ id: 1 }, { id: 2 }, { id: 3 }];
const idSet = new Set([1, 2, 3, 4, 5]);

for (let user of users) {
  if (idSet.has(user.id)) {
    console.log(user);
  }
}

5. DRY (Don't Repeat Yourself) - ไม่ซ้ำซ้อน

ถ้า copy-paste code 2 ครั้ง ให้สร้าง function:

// ❌ Bad: Repetitive code
const firstName = "John";
const lastName = "Doe";
const middleName = "Michael";

const firstInitial = firstName.charAt(0).toUpperCase();
const lastInitial = lastName.charAt(0).toUpperCase();
const middleInitial = middleName.charAt(0).toUpperCase();

// ✅ Good: Reusable function
function getInitial(name) {
  return name.charAt(0).toUpperCase();
}

const firstInitial = getInitial(firstName);
const lastInitial = getInitial(lastName);
const middleInitial = getInitial(middleName);

// Or even better: map
const names = ["John", "Doe", "Michael"];
const initials = names.map(getInitial);

6. Comments - ความเห็น

เขียน comments ที่อธิบาย "ทำไม" ไม่ใช่ "อะไร":

// ❌ Bad: Obvious comment
const age = 25; // Set age to 25
const users = []; // Create empty array

// ✅ Good: Explains why or complex logic
// Cache user preferences locally to reduce API calls
const userPreferences = localStorage.getItem("prefs");

// Retry 3 times because API sometimes fails temporarily
async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fetch(url);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await delay(1000 * Math.pow(2, i)); // Exponential backoff
    }
  }
}

7. Use Const by Default - ใช้ const ก่อน

ลำดับความสำคัญ: const > let > var (ไม่ใช้ var เลย):

// ✅ Hierarchy
const user = { name: "John" }; // Use const by default
let count = 0; // Use let when value changes
let index = 0;
for (const item of items) { // Use const in loops too
  console.log(item);
}

// ✅ Const prevents accidental reassignment
const PI = 3.14159;
const MAX_USERS = 100;
const API_URL = "https://api.example.com";

// ❌ Avoid
var oldVar = 5; // Don't use var

8. Async/Await over Callbacks - ใช้ async/await

Async/Await อ่านง่ายกว่า promise chains:

// ❌ Bad: Callback hell
function fetchUserData(userId, callback) {
  fetchUser(userId, (user) => {
    fetchPosts(user.id, (posts) => {
      fetchComments(posts[0].id, (comments) => {
        callback({ user, posts, comments });
      });
    });
  });
}

// Better: Promise chaining
function fetchUserData(userId) {
  return fetchUser(userId)
    .then(user => 
      fetchPosts(user.id)
        .then(posts => ({ user, posts }))
    )
    .then(data =>
      fetchComments(data.posts[0].id)
        .then(comments => ({ ...data, comments }))
    );
}

// ✅ Best: Async/Await (reads like synchronous)
async function fetchUserData(userId) {
  const user = await fetchUser(userId);
  const posts = await fetchPosts(user.id);
  const comments = await fetchComments(posts[0].id);
  
  return { user, posts, comments };
}

🎯 สรุปประเด็นสำคัญทั้งหมด

  • Variables: ใช้ const by default, let เมื่อต้อง reassign
  • Naming: ใช้ชื่อที่ชัดเจนและมีความหมาย
  • Functions: ทำหนึ่งสิ่งให้ดี (Single Responsibility)
  • Errors: จัดการ error ด้วย try/catch
  • DOM: Access DOM element เพียงครั้งเดียว
  • Async: ใช้ async/await แทน callbacks
  • DRY: ไม่ copy-paste code ซ้ำไป
  • Comments: อธิบาย "ทำไม" ไม่ใช่ "อะไร"

📚 บทสรุป

  • ✅ JavaScript ทำให้เว็บ interactive และ dynamic
  • ✅ ใช้ ES6+ features (const/let, arrow functions, classes)
  • ✅ DOM manipulation เปลี่ยนหน้า HTML
  • ✅ Event listeners ทำให้เว็บตอบสนอง user
  • ✅ Asynchronous code สำหรับ API calls
  • ✅ Error handling ป้องกันโปรแกรมขัด
  • ✅ Write clean, readable, maintainable code
  • ✅ Practice and experiment!