ใน JavaScript คุณจะได้ยินบ่อยว่า primitive values เป็น immutable (ไม่เปลี่ยนแปลงได้) ในขณะที่ non-primitive values เป็น mutable (เปลี่ยนแปลงได้) การเข้าใจความแตกต่างระหว่างประเภทค่าเหล่านี้ (เรียกอีกอย่างว่า ชนิดข้อมูล) สำคัญมาก เพราะจะช่วยหลีกเลี่ยงปัญหามากมายในอนาคต
Primitive values ได้แก่:
- ตัวเลข (numbers)
- สตริง (strings)
- บูลีน (booleans)
Non-primitive values ได้แก่:
- อ็อบเจ็กต์ (objects)
- ฟังก์ชัน (functions)
Mutable หมายถึง สามารถเปลี่ยนแปลงได้
Immutable หมายถึง ไม่สามารถเปลี่ยนแปลงได้
Non-primitive values เป็น mutable
ต่อไปนี้คือรายการ (อาร์เรย์) ของคนที่กำหนดให้ตัวแปร:
let peopleList = ['Arnold', 'Linda', 'Sylvester', 'Dolph']
ตอนนี้ผมต้องการ mutate (เปลี่ยนแปลง) รายการของผม Mutate หมายถึง เปลี่ยนแปลง อยากเปลี่ยน Arnold เป็น Jean-Claude และพิมพ์ผลลัพธ์:
let peopleList = ['Arnold', 'Linda', 'Sylvester', 'Dolph'] peopleList[0] = 'Jean-Claude' console.log(peopleList)
ได้แล้ว! เราเพิ่งทำการ mutate บนค่าที่มีอยู่ ในกรณีนี้ค่าที่มีอยู่คืออาร์เรย์ (รายการ) หรือเฉพาะเจาะจงกว่านั้นคือ array object
ใน JavaScript non-primitive values เป็น mutable (เปลี่ยนแปลงได้)
Primitive values เป็น immutable
ต่อไปนี้คือค่าข้อความ (string) ที่กำหนดให้ตัวแปร:
let person = 'Ernold'
อืม เหนื่อยนิดหน่อย แต่เราสะกด Arnold ผิด โดยใช้ E แทน A ในตัวอักษรแรก มาแก้ไขเร็วๆ แล้วพิมพ์ผลลัพธ์:
let person = 'Ernold' person[0] = 'A' console.log(person) // output: Ernold
อ๋อ ไทำไมมันพิมพ์ "Ernold" ออกมา? เราเพิ่งสลับ E เป็น A ไปแล้วจริงๆ!
ไม่ใช่ เพราะค่าที่เราพยายาม mutate เป็น string ซึ่งเป็น primitive value — และ primitive values ทั้งหมดเป็น immutable (ไม่เปลี่ยนแปลงได้)
มีเพียง non-primitive values (objects & functions) เท่านั้นที่เป็น mutable
งงไหม?
เป็นเรื่องปกติที่จะงงเรื่องนี้ตอนเริ่มเรียน JavaScript เพราะบนผิวแล้วตัวอย่างด้านบนดูเหมือนกันและพฤติกรรมคล้ายกัน — ใน บาง สถานการณ์
มาลองพิมพ์ชื่อแรกจาก อาร์เรย์ ก่อนหน้านี้:
let peopleList = ['Arnold', 'Linda', 'Sylvester', 'Dolph'] console.log(peopleList[0]) // output: "Arnold"
ไม่มีอะไรแปลก พิมพ์ "Arnold" ออกมา
ตอนนี้มาลองพิมพ์ตัวอักษรแรกของ สตริง จากก่อนหน้า (ชื่อที่สะกดผิด):
let person = 'Ernold' console.log(person[0]) // output: ??
คุณคิดว่าจะเกิดอะไรขึ้น?
...
...
ได้แล้ว! มันพิมพ์ "E" ออกมา!
คุณคาดหวังว่าจะไม่ได้ผลไหม?
ไม่ว่าจะอย่างไร บทเรียนสำคัญคือ คุณสามารถ เข้าถึง (access) primitive value ได้ในลักษณะคล้ายกับ non-primitive values แต่คุณ ไม่สามารถเปลี่ยนแปลง (mutate) มันได้ นั่นคือความแตกต่างใหญ่ (access vs. mutate)
ใน JavaScript primitive values เป็น read-only (อ่านอย่างเดียว) ในขณะที่ non-primitive values (objects/arrays, functions) เป็น read & write (อ่านและเขียนได้)
นั่นคือเหตุผลที่เราเปลี่ยน (mutate) ค่ารายการแรกของอาร์เรย์จาก Arnold เป็น Jean-Claude ได้ก่อนหน้านี้ แต่เราไม่สามารถแก้ข้อผิดพลาดการสะกดจาก "Ernold" เป็น "Arnold" — เพราะมันเป็นสตริง และสตริง/primitive values เป็น immutable
ตัวแปร vs. ค่า
(ผมรู้ว่าบางคนคิดอย่างนี้)
แม้เราจะสรุปว่า primitive values เป็น immutable ผมยังตั้งใจแก้ข้อผิดพลาดการสะกด "Ernold" มาลองอะไรใหม่!
ดูโค้ดต่อไปนี้ (อ่านอย่างละเอียด):
let person = 'Ernold' person = 'Arnold' console.log(person) // output: ??
ด้วยสิ่งที่คุณเรียนรู้เกี่ยวกับ primitive values เช่น สตริง คุณคิดว่าโค้ดด้านบนจะพิมพ์ Arnold ออกมาหรือไม่? ใช่หรือไม่?
...
(คิดดูสิ)
...
มันพิมพ์ Arnold จริงๆ!
บางคนอาจรู้เหตุผลแล้ว แต่ถ้ายังไม่รู้ก็ปกติ คุณจะได้ "อะฮะ โมเมนต์" ในทีเดียว อ่านย่อหน้าต่อไปอย่างละเอียด
โค้ดด้านบนทำงานได้เพราะเรา ไม่ได้ mutate ค่าสตริง เราแม้แต่จะไม่ได้สัมผัสมันเลย แทนที่จะเป็นนั้น เรากำหนดค่า ใหม่ คือสตริง 'Arnold' ให้ตัวแปร person ทำให้มันไม่ได้อ้างอิง (ชี้ไปที่) ค่าสตริง 'Ernold' อีกต่อไป
Primitive values ไม่สามารถเปลี่ยนแปลงได้ แต่ตัวแปรสามารถ!
ตัวแปรไม่ใช่ค่า ดังนั้นกฎของ mutability หรือ immutability จึงไม่เหมือนกัน ตัวแปรเพียงชี้ไปที่ค่าเท่านั้น
เราไม่ได้สัมผัสค่าสตริงเดิม 'Ernold' โดยตรง เราเพียงบอกตัวแปร person ให้ชี้ไปที่สตริงอีกตัวชื่อ 'Arnold' แล้วใช้ console.log() พิมพ์ผลลัพธ์
บางคนอาจคิดว่าผมคุยซ้ำๆ แต่การเข้าใจเรื่องนี้เกี่ยวกับวิธีการทำงานของ JavaScript สำคัญมาก การซ้ำคือเพื่อนของคุณ
หมายเหตุ: ประเภทตัวแปรที่ใช้ตลอดบทช่วยสอนนี้เป็น keyword type let — ซึ่งเป็น mutable ตามที่คุณเห็นในตัวอย่างก่อนหน้า หากใช้ตัวแปร const สถานการณ์จะต่างออกไป — แต่จะเก็บไว้สำหรับบทช่วยสอนอื่น
สรุป:
ใน JavaScript:
- non-primitive values สามารถอ้างอิง เข้าถึง — และ mutate ได้
- primitive values สามารถอ้างอิง เข้าถึง — แต่ ไม่สามารถ mutate ได้
- ตัวแปรและค่าเป็นสิ่งต่างกัน กฎที่แตกต่างกันใช้ได้