Cập nhật mảng bất biến bằng Array.prototype.with

Gần đây, các trình duyệt đã có một phương thức tương tác mới mà bạn có thể gọi trên Mảng: Array.prototype.with().

Browser Support

  • Chrome: 110.
  • Edge: 110.
  • Firefox: 115.
  • Safari: 16.

Source

Bài viết này khám phá cách hoạt động của phương thức này và cách sử dụng phương thức này để cập nhật một mảng mà không làm thay đổi mảng ban đầu.

Giới thiệu về Array.prototype.with(index, value)

Phương thức Array.prototype.with(index, value) trả về một bản sao của mảng mà phương thức này được gọi với index được đặt thành value mới mà bạn cung cấp.

Ví dụ sau đây cho thấy một mảng độ tuổi. Bạn muốn tạo một bản sao mới của mảng trong khi thay đổi độ tuổi thứ hai từ 15 thành 16:

const ages = [10, 15, 20, 25];

const newAges = ages.with(1, 16);
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)

Phân tích mã: ages.with(...) trả về bản sao của biến ages mà không sửa đổi mảng ban đầu. ages.with(1, …) thay thế mục thứ hai (index = 1). ages.with(1, 16) chỉ định mục thứ hai cho 16.

Đây là cách bạn có thể tạo một bản sao mới của mảng có nội dung đã được sửa đổi.

Điều này khá hữu ích khi bạn muốn đảm bảo rằng mảng ban đầu vẫn không thay đổi. Bài viết này đề cập đến một số trường hợp sử dụng cho việc này. Nhưng hiện tại, hãy xem điều gì sẽ xảy ra nếu bạn sử dụng ký hiệu dấu ngoặc vuông:

const ages = [10, 15, 20, 25];

const newAges = ages;
newAges[1] = 16;
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 16, 20, 25] (Also changed 🙁)

Như bạn có thể thấy, biến ages cũng được sửa đổi trong ví dụ này. Đó là vì khi bạn chỉ định ages = newAges, JavaScript không sao chép mảng mà tạo một tệp tham chiếu đến mảng khác. Vì vậy, mọi thay đổi trong một mảng cũng sẽ ảnh hưởng đến mảng còn lại vì cả hai đều trỏ đến cùng một mảng.

Array.prototype.with() và tính bất biến

Tính bất biến là cốt lõi của nhiều thư viện và khung giao diện người dùng, chẳng hạn như React (và redux) và Vue

Ngoài ra, các thư viện và khung khác không nhất thiết phải yêu cầu tính bất biến nhưng khuyến khích tính bất biến để có hiệu suất tốt hơn: Angular và Lit

Vì vậy, các nhà phát triển thường phải sử dụng những phương thức khác trả về bản sao của mảng, điều này làm giảm khả năng đọc mã:

const ages = [10, 15, 20, 25];

const newAges = ages.map((age, index) => {
    if (index === 1) {
         return 16;
    }
    return age;
});

console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (Remains unchanged)

Dưới đây là một ví dụ về Codepen về cách sử dụng .with() trong React kết hợp với useState để cập nhật một cách bất biến một mảng các mục:

Vì phương thức .with() trả về một bản sao của mảng, nên bạn có thể liên kết nhiều lệnh gọi .with() hoặc thậm chí cả các phương thức mảng khác. Ví dụ sau đây minh hoạ cách tăng độ tuổi thứ hai và thứ ba trong mảng:

const ages = [10, 15, 20, 25];

const newAges = ages.with(1, ages[1] + 1).with(2, ages[2] + 1)

console.log(newAges); // [10, 16, 21, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)

Các phương thức bất biến mới khác

Gần đây, 3 phương thức khác đã có thể tương tác với nhau:

Theo MDN, 3 phương thức này là phiên bản sao chép của các đối tượng tương ứng. Bạn cũng có thể dùng các phương thức này khi tính bất biến được mong đợi hoặc ưu tiên.

Tóm lại, bạn có thể dễ dàng đạt được các bản cập nhật bất biến trong JavaScript bằng một trong bốn phương thức được trình bày trong bài viết này. Cụ thể, phương thức .with() giúp bạn dễ dàng cập nhật một phần tử duy nhất của mảng mà không làm thay đổi mảng ban đầu.