Skip to content

บทที่ 03 — Operators: เครื่องหมายคำนวณและเปรียบเทียบ

← บทที่ 02 | สารบัญ | บทที่ 04: Control Flow →

Operator = "เครื่องหมาย" ที่ใช้ทำอะไรกับข้อมูล — บวก ลบ คูณ เปรียบเทียบ ฯลฯ

บทนี้สอน operator ทุกตัวที่ต้องรู้ พร้อมกับ "กับดัก" ที่ทำให้มือใหม่เขียนบั๊กบ่อย

อ่านจบบทนี้คุณจะ:

  • คำนวณเลข เปรียบเทียบค่า ได้ถูกต้อง
  • เข้าใจว่าทำไมต้องใช้ === ไม่ใช่ == (เรื่องสำคัญมาก)
  • ใช้ operator สมัยใหม่ ??, ?., ... ที่ทำให้โค้ดสั้นและปลอดภัย
  • เข้าใจ destructuring — เทคนิคที่ใช้ตลอดในงานจริง

ใช้เวลา 2-3 ชั่วโมง


Part 1: Arithmetic Operators — คำนวณเลข

1.1 พื้นฐาน

javascript
5 + 3        // 8   — บวก
5 - 3        // 2   — ลบ
5 * 3        // 15  — คูณ
5 / 3        // 1.6666666666666667  — หาร (ไม่มี integer division แยกต่างหาก — ถ้าหารไม่ลงตัวจะได้ทศนิยม)
5 % 3        // 2   — หารเอาเศษ (modulo)
5 ** 3       // 125 — ยกกำลัง (5³)

อันที่มือใหม่ไม่คุ้นคือ % (modulo) — มันคือ "เศษที่เหลือจากการหาร":

javascript
10 % 3       // 1   — 10 หาร 3 ได้ 3 เหลือเศษ 1
10 % 2       // 0   — 10 หาร 2 ลงตัว เหลือ 0

% มีประโยชน์มาก — เช่น เช็คเลขคู่/คี่:

javascript
8 % 2 === 0    // true  — 8 เป็นเลขคู่ (หาร 2 ลงตัว)
7 % 2 === 0    // false — 7 เป็นเลขคี่

1.2 เพิ่ม/ลด ทีละหนึ่ง

javascript
let x = 5;
x++;         // เพิ่ม 1 — x กลายเป็น 6
x--;         // ลด 1 — x กลายเป็น 5

กรณีทั่วไป — เขียน x++ เดี่ยว ๆ (ไม่ assign) ไม่ต้องกังวลอะไร:

javascript
let count = 0;
count++;         // count กลายเป็น 1 — ใช้แบบนี้บ่อยที่สุด

Edge case (ไม่ค่อยเจอ) — x++ (postfix) ต่างจาก ++x (prefix) เมื่อเอาค่าไป assign:

javascript
let a = 5;
let b = a++;     // b ได้ค่า 5 ก่อน แล้ว a ค่อยเพิ่มเป็น 6 (postfix: ใช้ก่อน เพิ่มทีหลัง)

let c = 5;
let d = ++c;     // c เพิ่มเป็น 6 ก่อน แล้ว d ได้ค่า 6 (prefix: เพิ่มก่อน ใช้ทีหลัง)

→ ถ้าไม่ได้เอาค่า x++ ไป assign ต่อ ก็ไม่ต้องสนใจความต่างนี้

1.3 Math — เครื่องมือคณิตศาสตร์

JavaScript มี object ชื่อ Math ที่รวมฟังก์ชันคณิตศาสตร์:

javascript
Math.round(3.7)          // 4   — ปัดเศษ
Math.floor(3.7)          // 3   — ปัดลง
Math.ceil(3.2)           // 4   — ปัดขึ้น
Math.abs(-5)             // 5   — ค่าสัมบูรณ์ (เอาเครื่องหมายลบออก)
Math.max(1, 5, 3)        // 5   — ค่ามากสุด
Math.min(1, 5, 3)        // 1   — ค่าน้อยสุด
Math.sqrt(16)            // 4   — รากที่สอง
Math.pow(2, 10)          // 1024 — ยกกำลัง (= 2 ** 10)
Math.random()            // เลขสุ่ม 0 ถึง 1 (ไม่ถึง 1)

สูตรที่ใช้บ่อย — สุ่มเลขจำนวนเต็ม 0 ถึง 9:

javascript
Math.floor(Math.random() * 10)

อธิบาย: Math.random() ได้ 0.xxx → คูณ 10 ได้ 0.x ถึง 9.x → Math.floor ปัดลง → ได้ 0-9


Part 2: Assignment Operators — กำหนดค่า

= คือการกำหนดค่า — แต่มีตัวย่อที่รวม "คำนวณ + กำหนดค่า":

javascript
let x = 10;

x += 5;      // เท่ากับ x = x + 5  → x เป็น 15
x -= 3;      // เท่ากับ x = x - 3  → x เป็น 12
x *= 2;      // เท่ากับ x = x * 2  → x เป็น 24
x /= 4;      // เท่ากับ x = x / 4  → x เป็น 6
x %= 4;      // เท่ากับ x = x % 4  → x เป็น 2

ตัวย่อพวกนี้ทำให้โค้ดสั้นและอ่านง่ายขึ้น — x += 5 อ่านว่า "เพิ่ม x ขึ้น 5"


Part 3: Comparison Operators — เปรียบเทียบ

operator เปรียบเทียบให้ผลเป็น boolean (true/false) — ใช้กับการตัดสินใจ

3.1 เปรียบเทียบมาก/น้อย

ตัวดำเนินการเปรียบเทียบ (>, <, >=, <=) ใช้เทียบค่าสองค่าแล้วคืนผลเป็น boolean (true/false) — เป็นพื้นฐานของการเขียนเงื่อนไข if:

javascript
5 > 3        // true   — มากกว่า
5 < 3        // false  — น้อยกว่า
5 >= 5       // true   — มากกว่าหรือเท่ากับ
5 <= 4       // false  — น้อยกว่าหรือเท่ากับ

3.2 ⚠️ เปรียบเทียบเท่ากัน — == vs === (เรื่องสำคัญที่สุดของบท)

JavaScript มี 2 วิธีเช็คว่า "เท่ากัน":

=== (เปรียบเทียบแบบเข้มงวด / strict equality — "เข้มงวด" = ชนิดต้องตรงด้วย)

  • เช็คว่า "ชนิดเหมือนกัน และ ค่าเหมือนกัน"
  • ไม่แปลงชนิดข้อมูล
javascript
5 === 5          // true
5 === "5"        // false — ชนิดต่างกัน (number กับ string)
"a" === "a"      // true
true === true    // true

== (เปรียบเทียบแบบหลวม / loose equality — "หลวม" = แปลงชนิดให้ก่อน)

  • เช็คแค่ "ค่าเหมือนกัน" — แปลงชนิดให้ก่อนเปรียบเทียบ
javascript
5 == 5           // true
5 == "5"         // true — ⚠️ แปลง "5" เป็น 5 ก่อน แล้วถึงเทียบ
true == 1        // true — ⚠️ แปลง true เป็น 1
0 == ""          // true — ⚠️ แปลกประหลาด
null == undefined // true

ปัญหาของ == คือ — กฎการแปลงชนิดของมันซับซ้อนและคาดเดายาก ทำให้เกิดบั๊กที่หาไม่เจอ:

javascript
0 == false       // true
0 == ""          // true
"" == false      // true
0 == "0"         // true
"0" == false     // true
null == 0        // false  — ⚠️ แต่ null == undefined เป็น true (ไม่สม่ำเสมอ)

กฎทอง — สำคัญที่สุดของบทนี้: ใช้ === และ !== เสมอ — ห้ามใช้ == และ != ยกเว้นมีเหตุผลพิเศษจริง ๆ

=== คาดเดาได้ — ชนิดต้องตรง ค่าต้องตรง จบ. == คือบ่อเกิดของบั๊ก

javascript
5 !== "5"        // true  — ไม่เท่ากัน (เพราะชนิดต่าง)
5 != "5"         // false — ⚠️ อย่าใช้

3.3 NaN — ค่าที่ไม่เท่ากับอะไรเลย แม้แต่ตัวเอง

จำ NaN จากบทที่ 02 ได้ไหม — มันมีพฤติกรรมแปลก:

javascript
NaN === NaN      // false! — NaN ไม่เท่ากับอะไรเลย แม้แต่ตัวเอง

ดังนั้นเช็ค NaN ด้วย === ไม่ได้ — ต้องใช้ Number.isNaN():

javascript
Number.isNaN(NaN)         // true  ← ใช้อันนี้
Number.isNaN(5)           // false

3.4 เปรียบเทียบ String

string เปรียบเทียบตามลำดับตัวอักษร (เทียบ Unicode codepoint — รหัสตัวอักษร):

javascript
"apple" < "banana"       // true  — a มาก่อน b
"a" < "b"                // true
"Z" < "a"                // true  — ⚠️ ตัวพิมพ์ใหญ่มีรหัสน้อยกว่าตัวพิมพ์เล็ก

จำง่าย ๆ: ตัวพิมพ์ใหญ่ A-Z มีรหัส Unicode 65-90 ส่วนตัวพิมพ์เล็ก a-z มีรหัส 97-122 — ตัวพิมพ์ใหญ่ ทุกตัว จึงมาก่อนตัวพิมพ์เล็กเสมอเวลาเทียบ

หมายเหตุ: การเทียบแบบนี้ใช้รหัส Unicode ดิบ — เหมาะกับ ASCII (a-z, A-Z) แต่ ไม่เหมาะกับภาษาไทยหรือภาษาอื่น เพราะลำดับ Unicode ไม่ตรงกับลำดับพจนานุกรม

ถ้าต้องเรียงคำไทยหรือเทียบแบบไม่สนตัวพิมพ์ใหญ่/เล็ก ใช้ String.prototype.localeCompare():

javascript
"ก".localeCompare("ข")           // -1 (ก มาก่อน ข)
"Apple".localeCompare("apple", "en", { sensitivity: "base" })   // 0 (ถือว่าเท่ากัน)

Part 4: Logical Operators — ตรรกะ

operator ตรรกะใช้รวมเงื่อนไขหลายอัน

4.1 && (และ), || (หรือ), ! (ไม่)

ตัวดำเนินการตรรกะใช้รวมหรือกลับเงื่อนไข — && (และ: จริงทั้งคู่ถึงจริง), || (หรือ: จริงตัวใดตัวหนึ่งก็จริง), ! (ไม่: กลับค่า):

javascript
true && true     // true   — && (และ): จริงทั้งคู่ถึงจะจริง
true && false    // false
true || false    // true   — || (หรือ): จริงอย่างน้อยตัวหนึ่งก็จริง
false || false   // false
!true            // false  — ! (ไม่): กลับค่า
!false           // true

ตัวอย่างใช้จริง:

javascript
let age = 20;
let hasLicense = true;

age >= 18 && hasLicense       // true — อายุถึง และ มีใบขับขี่
age < 18 || !hasLicense       // false

4.2 Short-circuit — && || คืน "ค่า" ไม่ใช่แค่ boolean

นี่เป็นจุดที่ JavaScript ต่างจากภาษาอื่น — && และ || จริง ๆ คืนค่าตัวใดตัวหนึ่ง ไม่ใช่แค่ true/false

|| คืนค่าแรกที่เป็น truthy (ถ้าไม่มีเลย คืนตัวสุดท้าย):

javascript
"Alice" || "default"           // "Alice"    — ตัวแรก truthy ก็คืนเลย
"" || "default"                // "default"  — ตัวแรก falsy เลยคืนตัวถัดไป
0 || 100                       // 100

// กรณีทุกตัว falsy — คืนตัวสุดท้าย
false || null || 0             // 0 (ตัวสุดท้าย เพราะไม่มีตัวไหน truthy เลย)

&& คืนค่าแรกที่เป็น falsy (ถ้าไม่มี คืนตัวสุดท้าย):

javascript
"Alice" && "Bob"         // "Bob"   — ตัวแรก truthy เลยไปดูตัวถัดไป
"" && "Bob"              // ""      — ตัวแรก falsy ก็คืนเลย

ทำไม "short-circuit" (ลัดวงจร)? — เพราะมันหยุดทันทีที่รู้คำตอบ ไม่ดูที่เหลือ

ประโยชน์: ใช้ || ตั้งค่าเริ่มต้น (default value)

javascript
function greet(name) {
  name = name || "คนแปลกหน้า";       // สำนวนเก่า — ถ้า name เป็น '' หรือ 0 จะได้ default ด้วย (อาจไม่ตั้งใจ)
  return "สวัสดี " + name;
}

⚠️ ระวัง: name || "default" จะใช้ default เมื่อ name เป็น "" หรือ 0 ด้วย — ซึ่งอาจไม่ใช่สิ่งที่ต้องการ ดู ?? ใน Part 4.3 สำหรับวิธีที่ปลอดภัยกว่า

4.3 Nullish Coalescing (ใช้ค่า default เมื่อค่าเป็น null หรือ undefined) — ??

|| มีปัญหา — มันถือว่า 0, "", false เป็น falsy ทั้งหมด ทำให้ default ผิด:

javascript
let count = 0;
let display = count || 10;       // display = 10 — ⚠️ ผิด! 0 เป็นค่าที่ถูกต้อง

operator ?? (nullish coalescing — feature ของ ES2020, รองรับกว้างมากแล้วใน Node.js และทุก browser สมัยใหม่) แก้ปัญหานี้ — มันใช้ค่า default เฉพาะเมื่อค่าเป็น null หรือ undefined เท่านั้น:

javascript
let count = 0;
let display = count ?? 10;       // display = 0 — ✅ ถูก! 0 ไม่ใช่ null/undefined

let name = null;
let n = name ?? "ไม่มีชื่อ";       // "ไม่มีชื่อ" — null เลยใช้ default

"" ?? "default"                   // ""        — "" ไม่ใช่ null/undefined
0 ?? 100                          // 0
null ?? 100                       // 100
undefined ?? 100                  // 100

กฎ: ใช้ ?? เมื่อต้องการ "default ถ้าไม่มีค่า (null/undefined)" — ใช้ || เมื่อต้องการ "default ถ้าค่า falsy"

ในงานสมัยใหม่ ส่วนใหญ่ใช้ ?? เพราะปลอดภัยกว่า

⚠️ ห้ามผสม ?? กับ ||/&& โดยไม่ใส่วงเล็บ:

javascript
a || b ?? c          // ❌ SyntaxError ตอน parse (โค้ดไม่รันเลย)
(a || b) ?? c        // ✅ ต้องใส่วงเล็บ

(ข้อนี้บังคับโดย JavaScript ตั้งแต่ตอน parse (ขั้นตอนที่ engine อ่านและตรวจโค้ดก่อนรันจริง) — ไม่ใช่ error ตอนรัน เพราะ JS อยากให้ผู้เขียนชัดเจนว่าตั้งใจให้ลำดับเป็นแบบไหน)

4.4 Logical Assignment — ||=, &&=, ??=

operator สมัยใหม่ (ES2021) ที่รวม "ตรรกะ + กำหนดค่า":

javascript
let x = null;
x ??= 10;            // ถ้า x เป็น null/undefined → ใส่ 10 — x กลายเป็น 10

let config = {};
// config.theme เป็น undefined (ยังไม่มี key นี้) → ตั้งเป็น "light"
config.theme ??= "light";    // config.theme = "light"
// ✅ ES2021: ||=, &&=, ??=

Part 5: Ternary Operator — if แบบสั้น

? : (ternary) คือ "if-else แบบบรรทัดเดียว":

javascript
condition ? ค่าถ้าจริง : ค่าถ้าเท็จ

ตัวอย่าง:

javascript
let age = 20;
let status = age >= 18 ? "ผู้ใหญ่" : "เด็ก";
console.log(status);     // "ผู้ใหญ่"

เทียบกับ if ปกติ (สองตัวอย่างนี้แสดงสองสไตล์แยกกัน — อย่ารันพร้อมกันในบล็อกเดียว):

javascript
// แบบที่ 1: if ปกติ — ยาวกว่า
let status;
if (age >= 18) {
  status = "ผู้ใหญ่";
} else {
  status = "เด็ก";
}
javascript
// แบบที่ 2: ternary — บรรทัดเดียว (เลือกใช้แบบใดแบบหนึ่ง)
let status = age >= 18 ? "ผู้ใหญ่" : "เด็ก";

ใช้ ternary เมื่อ "เลือกค่าจาก 2 ตัวเลือกง่าย ๆ" — อย่าใช้กับ logic ซับซ้อน (อ่านยาก)


Part 6: Spread กับ Rest — ... (3 จุด)

... คือ operator ที่ทรงพลังมาก ใช้ได้ 2 แบบ — ดูจาก "บริบท"

6.1 Spread — "แผ่ออก"

Spread เอา array/object มา "แผ่" ออกเป็นชิ้น ๆ

กับ Array:

javascript
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];        // [1, 2, 3, 4, 5] — แผ่ arr1 ออก แล้วเติม 4, 5
const copy = [...arr1];               // [1, 2, 3] — copy array

// รวม array
const a = [1, 2];
const b = [3, 4];
const merged = [...a, ...b];          // [1, 2, 3, 4]

กับ Object:

javascript
const user = { name: "Alice", age: 25 };
const copy = { ...user };                          // copy object
const updated = { ...user, age: 26 };               // copy + เปลี่ยน age
const merged = { ...defaults, ...userSettings };    // รวม object (ตัวหลังทับตัวหน้า)

(จำได้ไหม — บทที่ 02 เราใช้ { ...obj } copy object — นั่นคือ spread)

ส่งเป็น argument ของ function:

javascript
const numbers = [5, 1, 8, 3];
Math.max(...numbers);        // 8 — แผ่ array ออกเป็น Math.max(5, 1, 8, 3)

6.2 Rest — "รวบเข้า"

Rest ทำตรงข้าม — "รวบ" หลายค่ามาเป็น array เดียว

ใน parameter ของ function:

javascript
function sum(...numbers) {           // รวบทุก argument เป็น array ชื่อ numbers
  let total = 0;
  for (const n of numbers) total += n;     // for...of = วนแต่ละค่าใน array — จะเรียนละเอียดบทที่ 04
  return total;
}

sum(1, 2, 3);          // 6
sum(1, 2, 3, 4, 5);    // 15 — รับกี่ตัวก็ได้

💡 ถ้า for...of ยังดูแปลก — ตอนนี้รู้แค่ว่า "มันวนทุกค่าใน array" ก็พอ บทที่ 04 จะอธิบายครบ

จะ spread หรือ rest — ดูจากบริบท:

  • ... ตอน เรียกใช้/สร้าง = spread (แผ่ออก)
  • ... ตอน รับค่า/parameter = rest (รวบเข้า)

Part 7: Destructuring — ดึง property ออกจาก object/array เป็นตัวแปรในบรรทัดเดียว

ปัญหาก่อนมี destructuring: ถ้าต้องดึงค่าหลายตัวจาก object เดียวกัน ต้องเขียนซ้ำ ๆ:

javascript
const name = user.name;
const age = user.age;
const city = user.city;    // ยาวมากถ้ามีหลาย field

Destructuring (การแกะค่า — แยกค่าจาก array/object ออกเป็นตัวแปรย่อยทีละตัว) แก้ปัญหานี้ — ดึงหลายค่าพร้อมกันในบรรทัดเดียว ทำให้โค้ดสั้นและอ่านง่าย

7.1 Destructure Array

การแกะ array คือดึงค่าตามตำแหน่งออกมาเป็นตัวแปรในบรรทัดเดียว (const [a, b] = arr) แทนการเขียน arr[0], arr[1] ทีละตัว — สั้นและอ่านง่ายกว่า:

javascript
const colors = ["แดง", "เขียว", "น้ำเงิน"];

// แบบเก่า — ดึงทีละตัว
const c0 = colors[0];    // "แดง"
const c1 = colors[1];    // "เขียว"
javascript
const colors = ["แดง", "เขียว", "น้ำเงิน"];

// destructuring — แกะออกมาทีเดียว (สั้นกว่ามาก)
const [first, second, third] = colors;
console.log(first);      // "แดง"
console.log(second);     // "เขียว"

ลูกเล่น:

javascript
// ข้ามตัวที่ไม่เอา
const [, , third] = colors;          // third = "น้ำเงิน"

// ค่า default
const [a, b, c, d = "ดำ"] = colors;  // d = "ดำ" (ไม่มีตัวที่ 4)

// rest — รวบที่เหลือ
const [head, ...tail] = [1, 2, 3, 4];   // head = 1, tail = [2, 3, 4]

// สลับค่าตัวแปร (เทคนิคเด็ด)
let x = 1, y = 2;
[x, y] = [y, x];                      // x = 2, y = 1

💡 ฝึกก่อนไปต่อ: ลองรัน const [a, b] = [10, 20]; console.log(a, b); ใน Console — ถ้าได้ 10 20 แปลว่าเข้าใจ array destructuring แล้ว

7.2 Destructure Object

javascript
const user = { name: "Alice", age: 25, city: "Bangkok" };

// แบบเก่า
const n = user.name;     // "Alice"
const a = user.age;      // 25
javascript
const user = { name: "Alice", age: 25, city: "Bangkok" };

// destructuring — แกะตามชื่อ key (สั้นกว่ามาก)
const { name, age } = user;
console.log(name);       // "Alice"
console.log(age);        // 25

⚠️ ตอน destructure object — ชื่อตัวแปรต้องตรงกับ key

ลูกเล่น:

javascript
// เปลี่ยนชื่อตัวแปร
const { name: userName } = user;     // userName = "Alice"

// ค่า default
const { name, country = "Thailand" } = user;   // country = "Thailand" (ไม่มีใน object)

// rest
const { name, ...rest } = user;      // name = "Alice", rest = { age: 25, city: "Bangkok" }

// destructure object ซ้อน (ขั้นสูง — ถ้างงข้ามไปก่อนได้)
const data = { user: { profile: { email: "a@x.com" } } };
const { user: { profile: { email } } } = data;
console.log(email);      // "a@x.com"

7.3 Destructuring ใน Parameter ของ Function

ใช้บ่อยมาก — แกะค่าจาก object ที่ส่งเข้ามาเลย (เรื่อง function เรียนละเอียดบทที่ 05 — ตอนนี้รู้แค่ว่ามันคือ "ก้อนโค้ดที่เรียกใช้ซ้ำได้"):

javascript
// แบบเก่า
function greet(user) {
  return `สวัสดี ${user.name} อายุ ${user.age}`;
}

// destructure ใน parameter
function greet({ name, age }) {
  return `สวัสดี ${name} อายุ ${age}`;
}

greet({ name: "Alice", age: 25 });

→ destructuring คุณจะเห็นทุกที่ในโค้ด React, Node.js — ฝึกให้คล่อง


Part 8: Optional Chaining — ?.

ปัญหา: ถ้าเข้าถึง property ของ object ที่เป็น null/undefined → Error

javascript
const user = null;
user.name;               // ❌ TypeError: Cannot read properties of null (อ่าน property ของ null ไม่ได้)

operator ?. (optional chaining — เข้าถึง property แบบมีเงื่อนไข: ถ้าตัวก่อนหน้าเป็น null/undefined จะไม่ error; feature ของ ES2020, รองรับกว้างมากแล้วใน Node.js และทุก browser สมัยใหม่) แก้ปัญหานี้ — มันคืน undefined แทนที่จะ Error:

javascript
const user = null;
user?.name;              // undefined — ไม่ Error
user?.address?.city;     // undefined — เช็คได้หลายชั้น

ใช้กับ method และ array ได้ด้วย:

javascript
user?.greet?.();         // เรียก method ถ้ามี
user?.items?.[0];        // เข้าถึง array ถ้ามี

เปรียบเทียบกับวิธีเก่า:

javascript
// แบบเก่า — ยาว
const city = user && user.address && user.address.city;

// optional chaining — สั้น
const city = user?.address?.city;

?. มักใช้คู่กับ ??:

javascript
const city = user?.address?.city ?? "ไม่ระบุ";

→ "เอา city ถ้ามี ไม่งั้นใช้ 'ไม่ระบุ'" — ปลอดภัย ไม่มีทาง Error


Part 9: typeof, instanceof, in

operator พิเศษสำหรับเช็คชนิดและความเป็นเจ้าของ

9.1 typeof — เช็คชนิด

typeof ใช้ตรวจว่าค่าหนึ่งเป็นชนิดอะไร (คืนเป็น string เช่น "number") — มีประโยชน์เวลาเช็คก่อนทำงาน แต่ระวังกับดักในตำนาน typeof null ได้ "object":

javascript
typeof 42                // "number"
typeof "hi"              // "string"
typeof true              // "boolean"
typeof undefined         // "undefined"
typeof null              // "object"  ← บั๊กในตำนาน
typeof {}                // "object"
typeof []                // "object"
typeof function(){}      // "function"

// ใช้เช็คก่อนทำงาน
if (typeof callback === "function") {
  callback();
}

9.2 in — เช็คว่ามี key ไหม

in ใช้ตรวจว่า object มี key ที่ระบุหรือไม่ (คืน true/false) — ต่างจากการเช็คค่า เพราะ key ที่มีค่าเป็น undefined ก็ยังนับว่า "มี":

javascript
const user = { name: "Alice", age: 25 };

"name" in user           // true
"email" in user          // false

9.3 instanceof — เช็คว่าเป็น instance (ตัวอย่าง/วัตถุที่สร้างจาก class) ของ class ไหม

(เรื่อง class เรียนละเอียดบทที่ 06 — ตอนนี้รู้แค่ว่าเป็น "พิมพ์เขียวสำหรับสร้าง object")

javascript
[] instanceof Array              // true
new Date() instanceof Date       // true

⚠️ กับดัก instanceof — ใช้ข้าม realm ไม่ได้: ถ้า array ถูกสร้างมาจาก iframe คนละอัน, worker, หรือ Node vm module — instanceof Array จะคืน false แม้มันเป็น array จริง ๆ เพราะคนละ Array constructor

เช็ค array ที่แน่นอนกว่า typeof และ instanceof (ใช้ได้ข้าม realm):

javascript
Array.isArray([])        // true   ← ใช้อันนี้เช็ค array (ปลอดภัยกว่า instanceof)
Array.isArray("abc")     // false

Part 10: Operator Precedence — ลำดับการคำนวณ

operator มี "ลำดับความสำคัญ" — เหมือนคณิตศาสตร์ (คูณหารก่อนบวกลบ):

javascript
2 + 3 * 4        // 14 — คูณก่อน (3*4=12) แล้วบวก (2+12)
(2 + 3) * 4      // 20 — วงเล็บก่อน

ลำดับคร่าว ๆ (สูง → ต่ำ) — ตารางนี้ตัด bitwise operator ออกเพราะใช้น้อยในงานทั่วไป แต่ใส่ typeof/in/instanceof ไว้เพราะบทนี้สอนไปแล้ว:

  1. () วงเล็บ
  2. ** ยกกำลัง
  3. typeof, !, unary -/+ (เช่น -5, !true) — ตัวดำเนินการที่ใช้กับค่าเดียว
  4. * / %
  5. + -
  6. < > <= >=, in, instanceof
  7. === !==
  8. &&
  9. || ?? (มี precedence เดียวกัน แต่ห้ามผสมกันโดยไม่ใส่วงเล็บ — SyntaxError)
  10. ? : ternary
  11. = += -= *= ฯลฯ กำหนดค่า
  12. , (comma operator — ใช้น้อยมาก, คืนค่าตัวขวาสุด)

ตารางเต็ม (รวม bitwise operator เช่น &, |, ^) ดูได้ที่ MDN operator precedence

กฎทอง: ถ้าไม่แน่ใจลำดับ — ใส่วงเล็บ มันทำให้โค้ดชัดเจน อ่านง่าย ไม่ต้องจำลำดับ

javascript
// อ่านยาก
let result = a && b || c && d;

// ชัดเจน
let result = (a && b) || (c && d);

Part 11: Lab — ลงมือทำ

Lab 1: ทายผลลัพธ์

ลองทายผลก่อนรันแต่ละบรรทัด แล้วพิมพ์ลง Console เช็ค — โจทย์เน้นจุดที่สับสนบ่อย: == vs ===, NaN, และ || vs ??:

javascript
5 === "5"
5 == "5"
0 == false
0 === false
null == undefined
null === undefined
NaN === NaN
"" || "default"
"" ?? "default"
0 || 100
0 ?? 100

Lab 2: เครื่องคิดเลขส่วนลดด้วย ternary

ฝึกใช้ ternary operator (เงื่อนไข ? ค่าจริง : ค่าเท็จ) กับโจทย์คำนวณส่วนลดตามสถานะสมาชิก:

javascript
const price = 1000;
const isMember = true;

const discountRate = isMember ? 0.2 : 0.05;
const finalPrice = price * (1 - discountRate);

console.log(`ส่วนลด: ${discountRate * 100}%`);
console.log(`ราคาสุทธิ: ${finalPrice} บาท`);

Lab 3: Destructuring + default

ฝึกแกะ object ในพารามิเตอร์ฟังก์ชันพร้อมกำหนดค่า default — แพตเทิร์นที่ใช้บ่อยมากเวลารับ options:

javascript
function createUser({ name, role = "user", active = true } = {}) {
  return { name, role, active };
}

console.log(createUser({ name: "Alice" }));
console.log(createUser({ name: "Bob", role: "admin" }));
console.log(createUser());

อธิบายส่วนยุ่ง ๆ:

  • role = "user", active = true — ค่า default ถ้าไม่ส่งมาใน object
  • = {} ด้านนอกสุด — ค่า default ถ้าไม่ส่ง argument เลย (เช่นเรียก createUser() ตัวเปล่า) → ถ้าไม่มี = {} จะ error เพราะ JavaScript จะพยายาม destructure จาก undefined

Lab 4: Optional chaining

javascript
const response = {
  data: {
    user: { name: "Alice" }
  }
};

console.log(response?.data?.user?.name);        // "Alice"
console.log(response?.data?.order?.id);          // undefined
console.log(response?.data?.order?.id ?? "ไม่มี order");  // "ไม่มี order"

Part 12: Checkpoint

  1. % (modulo) ทำอะไร? ใช้เช็คเลขคู่ยังไง?
  2. x += 5 เท่ากับอะไร?
  3. === ต่าง == ยังไง? ควรใช้อันไหน? ทำไม?
  4. ทำไม NaN === NaN ได้ false? เช็ค NaN ยังไง?
  5. && กับ || คืน "ค่า" ไม่ใช่แค่ boolean — อธิบาย
  6. ?? ต่าง || ยังไง? ใช้อันไหนเมื่อไหร่?
  7. Spread กับ Rest ต่างกันยังไง? ดูจากอะไร?
  8. Destructuring คืออะไร? ยกตัวอย่าง
  9. ?. (optional chaining) แก้ปัญหาอะไร?
  10. ถ้าไม่แน่ใจลำดับ operator ควรทำยังไง?

Part 13: สรุปบทนี้

  • Arithmetic: + - * / % **% คือเศษจากการหาร
  • === เสมอ ไม่ใช่ == — เรื่องสำคัญที่สุด == แปลงชนิดทำให้เกิดบั๊ก
  • NaN เช็คด้วย Number.isNaN() (เพราะ NaN === NaN เป็น false)
  • && || คืน "ค่า" จริง ๆ ไม่ใช่แค่ boolean (short-circuit)
  • ?? = default ถ้า null/undefined / || = default ถ้า falsy → ใช้ ?? เป็นหลัก
  • ... = spread (แผ่ออก) หรือ rest (รวบเข้า) — ดูจากบริบท
  • Destructuring = แกะค่าจาก array/object — ใช้บ่อยมากในงานจริง
  • ?. = เข้าถึง property อย่างปลอดภัย ไม่ Error ถ้าเป็น null/undefined
  • ไม่แน่ใจลำดับ operator → ใส่วงเล็บ

บทต่อไป — Control Flow: ควบคุมการทำงานด้วย if และ loop


← บทที่ 02 | สารบัญ | บทที่ 04: Control Flow →