Immutable Variables

Immutable Variables trong lập trình đề cập đến các biến mà giá trị của chúng không thể thay đổi sau khi được gán lần đầu tiên. Trong JavaScript, khái niệm này chủ yếu liên quan đến các kiểu dữ liệu nguyên thủy và cách quản lý dữ liệu bất biến trong các chương trình.

1. Các khái niệm cơ bản về Immutable Variables:

Kiểu dữ liệu nguyên thủy là bất biến:

  • Các kiểu dữ liệu nguyên thủy trong JavaScript bao gồm: string, number, boolean, null, undefined, symbol, và bigint.
  • Các giá trị này là bất biến, tức là khi bạn thay đổi một giá trị thuộc loại nguyên thủy, bạn thực chất đang tạo ra một giá trị mới chứ không thay đổi giá trị ban đầu.
let str = "Hello";
str[0] = "h"; // Không thay đổi gì cả, vì string là bất biến
console.log(str); // "Hello"

Hằng số với const:

  • Khi bạn khai báo một biến với const, bạn tạo ra một biến tham chiếu đến một giá trị mà không thể bị gán lại. Điều này có nghĩa là bạn không thể gán lại biến đó với một giá trị khác, nhưng điều này không làm cho đối tượng hoặc mảng mà biến const tham chiếu đến trở nên bất biến.
const x = 10;
x = 20; // Lỗi: Assignment to constant variable.

const obj = { a: 1 };
obj.a = 2; // Hợp lệ: Bạn không thay đổi tham chiếu của 'obj', chỉ thay đổi thuộc tính của nó.
console.log(obj.a); // 2

2. Đối tượng và Mảng là Mutable:

Trong JavaScript, đối tượng (object) và mảng (array) là các kiểu dữ liệu tham chiếu và có thể thay đổi (mutable), ngay cả khi chúng được khai báo bằng const. Điều này có nghĩa là bạn có thể thay đổi nội dung của đối tượng hoặc mảng, nhưng không thể thay đổi tham chiếu đến chúng.

const arr = [1, 2, 3];
arr.push(4); // Hợp lệ: Bạn đang thay đổi nội dung của mảng
console.log(arr); // [1, 2, 3, 4]

const obj = { key: 'value' };
obj.newKey = 'newValue'; // Hợp lệ: Bạn đang thêm thuộc tính mới vào đối tượng
console.log(obj); // { key: 'value', newKey: 'newValue' }

3. Làm việc với dữ liệu bất biến:

Để quản lý dữ liệu bất biến trong các chương trình phức tạp, đặc biệt là trong lập trình hàm hoặc khi xây dựng các ứng dụng với dữ liệu trạng thái (state), việc giữ cho dữ liệu không thay đổi có thể giúp tránh các lỗi khó phát hiện và tạo ra các ứng dụng dễ bảo trì hơn. Dưới đây là một số kỹ thuật phổ biến:

Sao chép đối tượng hoặc mảng thay vì thay đổi trực tiếp:

Thay vì thay đổi trực tiếp, bạn có thể tạo ra một bản sao của đối tượng hoặc mảng trước khi thực hiện thay đổi.

const arr = [1, 2, 3];
const newArr = [...arr, 4]; // Sử dụng Spread Operator để tạo ra một mảng mới
console.log(arr); // [1, 2, 3]
console.log(newArr); // [1, 2, 3, 4]

const obj = { a: 1, b: 2 };
const newObj = { ...obj, c: 3 }; // Tạo một đối tượng mới với thuộc tính mới
console.log(obj); // { a: 1, b: 2 }
console.log(newObj); // { a: 1, b: 2, c: 3 }

Object.freeze():

  • Object.freeze() là một phương pháp có sẵn trong JavaScript giúp ngăn không cho một đối tượng bị thay đổi. Sau khi đối tượng bị đóng băng (frozen), bạn không thể thêm, sửa đổi, hoặc xóa các thuộc tính của nó.
const obj = { a: 1 };
Object.freeze(obj);

obj.a = 2; // Không có hiệu lực, vì đối tượng đã bị đóng băng
console.log(obj.a); // 1
  • Tuy nhiên, Object.freeze() chỉ thực hiện việc đóng băng trên một mức (shallow freeze). Nếu đối tượng chứa các đối tượng khác (nested objects), thì các đối tượng con vẫn có thể bị thay đổi.
const obj = {
    a: 1,
    nested: {
        b: 2
    }
};
Object.freeze(obj);

obj.nested.b = 3; // Hợp lệ, vì đối tượng con không bị đóng băng
console.log(obj.nested.b); // 3

Deep Freeze (Đóng băng sâu):

Nếu bạn cần đóng băng một đối tượng bao gồm cả các đối tượng con, bạn có thể sử dụng kỹ thuật deep freeze.

function deepFreeze(object) {
    Object.freeze(object);
    Object.keys(object).forEach(key => {
        if (typeof object[key] === 'object' && object[key] !== null) {
            deepFreeze(object[key]);
        }
    });
}

const obj = {
    a: 1,
    nested: {
        b: 2
    }
};
deepFreeze(obj);

obj.nested.b = 3; // Không hợp lệ, vì đối tượng con cũng đã bị đóng băng
console.log(obj.nested.b); // 2

4. Sử dụng thư viện để làm việc với dữ liệu bất biến:

Một số thư viện như Immutable.js, Immer, hoặc Mori cung cấp các cấu trúc dữ liệu bất biến mạnh mẽ và tối ưu để quản lý dữ liệu phức tạp trong các ứng dụng.

// Sử dụng Immer để làm việc với dữ liệu bất biến
import produce from 'immer';

const baseState = [
    { todo: "Learn ES6", done: true },
    { todo: "Try Immer", done: false }
];

const nextState = produce(baseState, draftState => {
    draftState.push({ todo: "Tweet about it" });
    draftState[1].done = true;
});

console.log(baseState); // Không bị thay đổi
console.log(nextState); // Trạng thái mới với thay đổi

5. Kết luận

  • Immutable Variables giúp giữ cho dữ liệu của bạn không bị thay đổi một cách không mong muốn, đặc biệt quan trọng trong lập trình hàm và quản lý trạng thái.
  • Đối với các giá trị nguyên thủy, bất biến là bản chất tự nhiên của chúng.
  • Đối với đối tượng và mảng, bạn có thể sử dụng các kỹ thuật như Object.freeze(), deep freeze, hoặc các thư viện như Immutable.js và Immer để đảm bảo tính bất biến của dữ liệu trong ứng dụng.

Bài viết liên quan

Lý thuyết về Cookie & Session & Localstorage

18-10-2024 Admin
22 views + likes

Cookie thích hợp cho việc lưu trữ thông tin nhỏ và ngắn hạn, Session là lựa chọn tốt để lưu trữ thông tin bảo mật như phiên làm việc

Network request trong JS

10-10-2024 Admin
29 views + likes

Network request trong JS

Xử lý bất đồng bộ với callback, promise, và async/await trong JavaScript

27-09-2024 Admin
32 views + likes

Bất đồng bộ là một khái niệm quan trọng trong lập trình, đặc biệt là trong các ứng dụng web và xử lý tác vụ như tải dữ liệu từ máy chủ, đọc file từ hệ thống

Cài đặt php 8.2 cho ubuntu

24-09-2024 Admin
27 views + likes

Để cài đặt PHP 8.2 trên Ubuntu, bạn có thể làm theo các bước sau:

Javascript cơ bản

13-09-2024 Admin
107 views + likes

Nhập môn javascript