เปลี่ยนแปลงได้และไม่สามารถเปลี่ยนแปลงได้ใน TypeScript

เปลี่ยนแปลงได้และไม่สามารถเปลี่ยนแปลงได้ใน TypeScript

บทความนี้อธิบายแนวคิดเรื่องเปลี่ยนแปลงได้และไม่สามารถเปลี่ยนแปลงได้ใน TypeScript

YouTube Video

เปลี่ยนแปลงได้และไม่สามารถเปลี่ยนแปลงได้ใน TypeScript

อะไรคือสิ่งที่เปลี่ยนแปลงได้?

เปลี่ยนแปลงได้ หมายความว่าค่า สามารถเปลี่ยนแปลงได้ ชนิดข้อมูลแบบอ้างอิง เช่น วัตถุและอาร์เรย์ เป็นตัวอย่างที่ชัดเจนของโครงสร้างข้อมูลที่เปลี่ยนแปลงได้

ตัวอย่างของวัตถุที่เปลี่ยนแปลงได้

1type Person = { name: string; age: number };
2
3// Mutable Example: Object
4let person: Person = { name: "Alice", age: 25 };
5person.age = 26;
6console.log(person); // { name: "Alice", age: 26 }

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

ตัวอย่างของอาร์เรย์ที่เปลี่ยนแปลงได้

1// Mutable Example: Array
2let numbers: number[] = [1, 2, 3];
3numbers.push(4);
4console.log(numbers); // [1, 2, 3, 4]

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

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

1// Mutable Example: Function
2function append(arr: number[], value: number): void {
3    arr.push(value); // Modify the original array
4    console.log(arr);
5}
6
7let nums: number[] = [1, 2, 3];
8append(nums, 4);
9console.log(nums); // [1, 2, 3, 4]

เมื่อปฏิบัติการเปลี่ยนแปลงได้เกิดขึ้นในฟังก์ชัน อาร์เรย์ต้นฉบับก็จะถูกเปลี่ยนแปลงด้วย

อะไรคือสิ่งที่ไม่สามารถเปลี่ยนแปลงได้?

ไม่สามารถเปลี่ยนแปลงได้ หมายความว่าค่า ไม่สามารถถูกเปลี่ยนแปลงได้ ชนิดข้อมูลดั้งเดิม เป็นสิ่งที่ไม่สามารถเปลี่ยนแปลงได้โดยพื้นฐาน

ตัวอย่างของชนิดข้อมูลดั้งเดิมที่ไม่สามารถเปลี่ยนแปลงได้

1// Immutable Example: String
2let str: string = "hello";
3str[0] = "H"; // Error: Index assignment is not allowed
4console.log(str); // "hello"

ความพยายามที่จะเปลี่ยนอักขระแรกของสตริง str เป็น H จะล้มเหลว เพราะสตริงเป็นข้อมูลที่ ไม่สามารถเปลี่ยนแปลงได้

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

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

เนื่องจากตัวเลขไม่สามารถเปลี่ยนแปลงได้ การปฏิบัติการในฟังก์ชันจึงไม่ทำให้ตัวแปรต้นฉบับเปลี่ยนแปลง

ปฏิบัติการที่ไม่สามารถเปลี่ยนแปลงได้ในอาร์เรย์

อาร์เรย์เป็นสิ่งที่เปลี่ยนแปลงได้ แต่ โดยการสร้างอาร์เรย์ใหม่แทนการเปลี่ยนอาร์เรย์ต้นฉบับ เราสามารถทำให้เป็นปฏิบัติการที่ไม่สามารถเปลี่ยนแปลงได้

1// Create an array of numbers
2let numbers: number[] = [1, 2, 3];
3
4// Immutable Example: Creating a new array
5let newNumbers: number[] = [...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: Readonly<{ profile: { name: string } }> = Object.freeze({
3    profile: { name: "Bob" }
4});
5
6// Attempt to modify a nested property (this works because Object.freeze() is shallow)
7user.profile.name = "Charlie"; // No TypeScript error, but still mutable
8
9console.log(user.profile.name); // "Charlie" (nested object is still mutable)

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

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze<T>(obj: T): Readonly<T> {
 3    Object.freeze(obj);
 4    Object.getOwnPropertyNames(obj).forEach(prop => {
 5        const value = (obj as any)[prop];
 6        if (typeof value === "object" && value !== null) {
 7            deepFreeze(value);
 8        }
 9    });
10    return obj;
11}
12
13// Create a deeply frozen object
14const user = deepFreeze({
15  profile: { name: "Bob" }
16});
17
18// Attempt to modify a nested property
19// (this will now throw an error in strict mode)
20user.profile.name = "Charlie";  // No TypeScript error, but modification is not allowed at runtime
21
22console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

สรุป

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

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

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

YouTube Video