Mutable และ Immutable ใน JavaScript

Mutable และ Immutable ใน JavaScript

บทความนี้อธิบายแนวคิดเกี่ยวกับ mutable และ immutable ใน JavaScript

YouTube Video

Mutable และ Immutable ใน JavaScript

Mutable คืออะไร?

Mutable หมายถึงค่าที่สามารถเปลี่ยนแปลงได้ วัตถุและอาร์เรย์ซึ่งเป็น reference types เป็นตัวอย่างของโครงสร้างข้อมูลที่เป็น mutable

ตัวอย่างของวัตถุที่เป็น Mutable

1let person = { name: "Alice", age: 25 };
2person.age = 26;
3console.log(person); // { name: "Alice", age: 26 }

ในโค้ดนี้ property age ของวัตถุ person ถูกเปลี่ยนจาก 25 เป็น 26 เนื่องจากวัตถุถูก ส่งผ่านโดยการอ้างอิง ข้อมูลที่อยู่ที่ ตำแหน่งในหน่วยความจำ ที่เก็บอยู่ในตัวแปร person จะถูกเปลี่ยนแปลง

ตัวอย่างของอาร์เรย์ที่เป็น Mutable

1let numbers = [1, 2, 3];
2numbers.push(4);
3console.log(numbers); // [1, 2, 3, 4]

ในโค้ดนี้ใช้เมธอด push เพื่อเพิ่มองค์ประกอบใหม่ 4 เข้าไปในอาร์เรย์เดิม สิ่งนี้ เปลี่ยนอาร์เรย์เดิม ทำให้นี่เป็นการกระทำที่ mutable

ตัวอย่างในฟังก์ชัน

 1// Function to append a value to an array
 2function append(arr, value) {
 3    arr.push(value); // Modify the original array
 4    console.log(arr);
 5}
 6
 7let numbers = [1, 2, 3];
 8append(numbers, 4);
 9
10console.log(numbers); // [1, 2, 3, 4] (original array is modified)

เมื่อทำการกระทำแบบ mutable ภายในฟังก์ชัน อาร์เรย์เดิมก็จะถูกเปลี่ยนแปลงด้วย

Immutable คืออะไร?

Immutable หมายถึงค่าที่ไม่สามารถเปลี่ยนแปลงได้ Primitive types เป็นชนิดข้อมูลพื้นฐานที่โดยธรรมชาติแล้วเป็น immutable

ตัวอย่างของ Primitive Type ที่เป็น Immutable

1let str = "hello";
2str[0] = "H";
3console.log(str); // "hello"

การพยายามเปลี่ยนอักขระตัวแรกของสตริง str เป็น H ล้มเหลวเพราะสตริงเป็น immutable

ตัวอย่างในฟังก์ชัน

 1// Function to increment a number
 2function increment(num) {
 3    num++; // This modifies only the local copy of num
 4    console.log(num);
 5}
 6
 7let number = 10;
 8increment(number);
 9
10console.log(number); // 10 (original number remains unchanged)

เนื่องจากตัวเลขเป็น immutable การดำเนินการภายในฟังก์ชันจะไม่ส่งผลต่อค่าของตัวแปรต้นฉบับ

การกระทำที่เป็น Immutable บนอาร์เรย์

อาร์เรย์เป็น mutable แต่การ สร้างอาร์เรย์ใหม่แทนที่จะเปลี่ยนแปลงอาร์เรย์เดิม ทำให้สามารถทำการกระทำที่เป็น immutable ได้

1// Create an array of numbers
2let numbers = [1, 2, 3];
3
4// Create a new array by spreading the original and adding a new element
5let newNumbers = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)

ที่นี่ สเปรดซินแท็กซ์ (...) ถูกใช้เพื่อสร้างอาเรย์ใหม่ newNumbers เนื่องจากอาเรย์ numbers ดั้งเดิมไม่ได้ถูกแก้ไข การดำเนินการนี้จึงเป็นรูปแบบของการไม่เปลี่ยนแปลง

ประโยชน์ของการใช้โครงสร้างข้อมูลที่ไม่เปลี่ยนแปลง

ปรับปรุงความสามารถในการคาดการณ์

เนื่องจากข้อมูลที่ไม่เปลี่ยนแปลงไม่สามารถเปลี่ยนแปลงได้ การแก้ไขโดยไม่ได้คาดการณ์จึงมีโอกาสน้อย ซึ่งช่วยลดความเสี่ยงของข้อผิดพลาด

ความเข้ากันได้กับไลบรารีที่ยึดตามความไม่เปลี่ยนแปลง

ไลบรารีเช่น React และ Redux มักถูกออกแบบโดยมีข้อมูลที่ไม่เปลี่ยนแปลงเป็นพื้นฐาน ทำให้การจัดการสถานะง่ายขึ้นเมื่อใช้อย่างถูกต้อง

การทำให้ออบเจกต์ไม่เปลี่ยนแปลงด้วย Object.freeze

Object.freeze สามารถใช้เพื่อป้องกันการแก้ไขออบเจกต์

1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }

อย่างไรก็ตาม Object.freeze ทำการ แช่แข็งแบบตื้น ซึ่งหมายถึงคุณสมบัติของออบเจกต์ที่ซ้อนอยู่ยังคงสามารถเปลี่ยนแปลงได้

1// Create a frozen object with a nested object
2const user = Object.freeze({ profile: { name: "Bob" } });
3
4// Attempt to modify a nested property (this works because Object.freeze() is shallow)
5user.profile.name = "Charlie";
6
7console.log(user.profile.name); // "Charlie" (nested object is still mutable)

เพื่อสร้างออบเจกต์ที่ไม่เปลี่ยนแปลงอย่างสมบูรณ์ จำเป็นต้องใช้ การแช่แข็งแบบลึก

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze(obj) {
 3  Object.keys(obj).forEach(key => {
 4    if (typeof obj[key] === "object" && obj[key] !== null) {
 5      deepFreeze(obj[key]); // Recursively freeze nested objects
 6    }
 7  });
 8  return Object.freeze(obj); // Freeze the top-level object
 9}
10
11// Create a deeply frozen object
12const user = deepFreeze({ profile: { name: "Bob" } });
13
14// Attempt to modify a nested property (ignored)
15user.profile.name = "Charlie";
16
17console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

สรุป

  • ข้อมูล ที่เปลี่ยนแปลงได้ สามารถถูกแก้ไขได้ รวมถึงออบเจกต์และอาเรย์
  • ข้อมูล ที่ไม่เปลี่ยนแปลงได้ ไม่สามารถถูกแก้ไขได้ รวมถึงประเภทพื้นฐานเช่นสตริงและตัวเลข
  • การใช้สเปรดซินแท็กซ์หรือ map ช่วยให้สามารถดำเนินการกับข้อมูลที่ไม่เปลี่ยนแปลงได้
  • Object.freeze และ deepFreeze สามารถใช้เพื่อป้องกันการแก้ไขออบเจกต์
  • การใช้ข้อมูลที่ไม่เปลี่ยนแปลงช่วยให้โค้ดที่คาดการณ์ได้ง่ายและมีข้อผิดพลาดน้อยลง

การออกแบบที่ไม่เปลี่ยนแปลงช่วยเพิ่มความปลอดภัยและความอ่านง่ายของโค้ด ดังนั้นใช้ประโยชน์จากมันให้เต็มที่!

คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย

YouTube Video