Reference vs Value
mental model สำคัญที่สุดของ JS คือ primitive copy by value ส่วน object copy by reference เหตุผลที่แก้ array ใน function แล้ว caller โดนแก้ด้วย
ลองทาย: เขียน function เพิ่มของลง shopping cart
function addItem(cart) {
cart.push("item-1")
}
const myCart = []
addItem(myCart)
console.log(myCart) // [] หรือ ["item-1"] ?คำตอบคือ [“item-1”] myCart โดนแก้ แม้ฟังก์ชันรับเป็น parameter ใหม่ชื่อ cart
แต่ถ้าเปลี่ยนเป็น number รูปแบบเดียวกันกลับ ไม่โดนแก้
function addOne(n) {
n = n + 1
}
const myCount = 5
addOne(myCount)
console.log(myCount) // 5 ← ไม่ใช่ 6เกิดอะไรขึ้น ทำไมข้อแรกแก้ได้ ข้อสองแก้ไม่ได้ คำตอบคือ reference vs value ซึ่งเป็นวิธีคิดที่สำคัญที่สุดของ JS
2 กลุ่มของ type
ทบทวน lesson 2: JS มี 8 types แบ่งเป็น primitive 7 ตัว และ object 1 ตัว การแบ่งนี้ สำคัญมาก ตรงนี้
- Primitive (number, string, boolean, null, undefined, bigint, symbol) ใช้ copy by value
- Object (object, array, function, Date และอื่นๆ) ใช้ copy by reference
วิธีคิด: label กับของจริง
คิดง่ายๆ ว่า variable คือ label ที่ติดอยู่บนของ
- Primitive คือ label ติดบน กล่องที่เก็บค่า 1 ค่า copy label ก็ได้กล่องใหม่ (ค่าเหมือนเดิมแต่กล่องคนละกล่อง)
- Object คือ label ติดบน ตัวของ copy label ได้แค่ label ใหม่ที่ชี้ของชิ้นเดิม
let a = 5
let a = { count: 5 }aแต่ละกล่อง เก็บค่าเอง ไม่เกี่ยวกัน
a{ count: 5 }ยังไม่มี b มีแต่ a
ผลกระทบ: function แก้ข้อมูลของ caller ได้
เพราะ object ใช้ copy by reference เวลาส่งเข้า function ตัว function ได้ label เดียวกัน ที่ชี้ของชิ้นเดิม แก้ข้างในก็เท่ากับของจริงเปลี่ยน caller เห็นการเปลี่ยนด้วย
function addTax(price) {
return price + price * 0.07
}
let myPrice = 100
addTax(myPrice)
// myPrice = ?myPrice =100ก่อนรัน myPrice = 100
function addItem(arr) {
arr.push("C")
}
let myList = ["A", "B"]
addItem(myList)
// myList = ?myList =["A", "B"]ก่อนรัน myList = ["A", "B"]
addItem([...myList]) หรือออกแบบ function ให้ return ค่าใหม่แทน mutate ของเดิม=== กับ object ไม่ทำงานตามที่คิด
ทบทวน === เปรียบเทียบ value กับ type แต่กับ object เปรียบเทียบ reference ไม่ใช่ content
// primitive เทียบค่า ตามที่คาด
5 === 5 // true
"hi" === "hi" // true
// object เทียบ reference (ของชิ้นเดียวกันรึเปล่า)
{} === {} // false (object ใหม่ 2 ชิ้น)
[1, 2] === [1, 2] // false (array ใหม่ 2 ชิ้น)
const a = { x: 1 }
const b = a
a === b // true (ของชิ้นเดียวกัน)Shallow copy กับ Deep copy
... spread (lesson 6) copy แค่ 1 ชั้น เรียกว่า shallow copy ถ้า object มี nested object ข้างใน ตัว nested ยังใช้ reference ร่วมกันอยู่
const original = {
name: "Top",
hobbies: ["coding", "music"]
}
const shallow = { ...original }
shallow.name = "Mint" // ✓ original.name ยังเป็น "Top"
shallow.hobbies.push("running") // ⚠️ original.hobbies โดนแก้ด้วย!
// deep copy ที่ปลอดภัย
const deep = structuredClone(original)
deep.hobbies.push("running") // ✓ original.hobbies ไม่โดนแก้const กับ object ทบทวนจาก lesson 6
ตรงนี้เคลียร์: const ห้าม เปลี่ยน reference แต่ไม่ห้าม เปลี่ยนของข้างใน
const arr = [1, 2, 3]
arr.push(4) // ✓ OK เพราะแก้ของข้างใน reference ยังเดิม
arr[0] = 99 // ✓ OK เช่นกัน
arr = [5, 6, 7] // ❌ Error เพราะเปลี่ยน reference ทั้ง chunkถ้าต้องการห้ามแก้ข้างในด้วย ใช้ Object.freeze(arr) แต่ทั่วไปไม่จำเป็น
สรุป
- Primitive ใช้ copy by value (กล่องคนละกล่อง)
- Object / Array / Function ใช้ copy by reference (label ชี้ของชิ้นเดียวกัน)
- Function รับ object แล้วแก้ข้างใน caller ก็โดน เป็นรูปแบบที่ก่อ bug บ่อย
===กับ object เทียบ reference ไม่ใช่ content คือ{} === {}เป็น false- Spread
...เป็น shallow copy 1 ชั้น ใช้structuredCloneสำหรับ deep constห้าม reassign ตัวแปร แต่ไม่ห้ามแก้ของข้างใน
จบ Phase 2 แล้ว รู้พื้นฐานครบทั้ง syntax โครงสร้างข้อมูล และวิธีคิดเรื่อง reference จาก lesson ถัดไปเข้า Phase 3 ที่เป็น “JS แท้ๆ” เริ่มที่ array methods (map / filter / reduce) ซึ่งเป็นรูปแบบที่ทุก codebase JS ใช้