Một đoạn code cần kiểm tra ID đã được chọn hay chưa. Bạn có thể giữ mảng rồi gọi includes, hoặc tạo Set rồi gọi has. Quyết định không chỉ nằm ở một phép tra cứu; còn có cách cập nhật dữ liệu, ý nghĩa phần tử trùng và thời điểm tạo cấu trúc phụ.
Bài đánh giá hai lựa chọn bằng code chạy trên Node.js 24.18.1, tài liệu đối chiếu ngày 26/10/2026. Các fixture chỉ kiểm tra hành vi, không có benchmark thời gian hoặc bộ nhớ. Không có ngưỡng kích thước chung để bài viết yêu cầu mọi dự án phải đổi sang Set.
1. Hai cách kiểm tra phần tử có mặt
MDN về Array.includes và Set mô tả cách so sánh SameValueZero. Những giá trị như NaN được nhận diện, còn số 7 và chuỗi '7' vẫn khác nhau. Object được nhận diện theo tham chiếu, không theo các field bên trong.
Lưu thành membership-demo.mjs:
const selected = [7, 7, 12, NaN];
const lookup = new Set(selected);
console.log(selected.length, lookup.size);
console.log(selected.includes(7), lookup.has(7));
console.log(selected.includes('7'), lookup.has('7'));
console.log(selected.includes(NaN), lookup.has(NaN));
const original = {id: 7};
const objects = new Set([original]);
console.log(objects.has(original), objects.has({id: 7}));
Output đã kiểm tra là 4 3, true true, false false, true true, rồi true false. Set không giữ hai lần cùng giá trị 7, và object mới có cùng ID vẫn là một tham chiếu khác.
Nếu cần membership theo ID của object, hãy tạo cấu trúc từ ID theo một kiểu thống nhất. Đừng đưa object vào Set rồi kỳ vọng nó tự so sánh thuộc tính id. Cũng không tự đổi mọi ID sang string nếu hợp đồng dữ liệu đang phân biệt hai kiểu.
2. Tập tra cứu không tự theo mảng gốc
const ids = [7, 12];
const lookupSnapshot = new Set(ids);
ids.push(20);
console.log(ids.includes(20), lookupSnapshot.has(20));
lookupSnapshot.add(20);
console.log(lookupSnapshot.has(20));
Kết quả đã chạy là true false, rồi true. Việc tạo Set từ mảng không thiết lập một liên kết tự đồng bộ. Nếu giữ cả hai biểu diễn, bạn cần quy tắc cập nhật rõ ràng hoặc tái tạo cấu trúc tại một ranh giới đã chọn.
Trong UI, điều quan trọng là biết đâu là nguồn dữ liệu chính. Một Set tra cứu được cache quá lâu có thể trả lời sai dù thao tác has rất nhanh. Ngược lại, tạo Set mới ở mọi lần kiểm tra đơn lẻ có thể làm bạn trả chi phí chuyển đổi nhiều lần mà không cần thiết.
3. So sánh theo cùng nhóm tiêu chí
Về độ dễ bắt đầu, includes phù hợp khi dữ liệu vốn là mảng và chỉ có ít chỗ hỏi một giá trị có tồn tại không. Has cần bạn đã có Set hoặc thêm bước tạo Set. Đoạn code ngắn hơn không tự cho biết cả luồng đơn giản hơn; hãy nhìn nơi cấu trúc được tạo và được cập nhật.
Về quyền kiểm soát, mảng giữ phần tử trùng và vị trí index, còn Set biểu diễn các giá trị duy nhất. Nếu số lần xuất hiện có ý nghĩa, chuyển toàn bộ dữ liệu sang Set có thể làm mất thông tin cần thiết. Có thể giữ mảng để hiển thị và một Set để tra cứu, nhưng khi đó phải quản lý nhất quán hai biểu diễn.
Về hệ sinh thái, cả hai là tính năng JavaScript chuẩn và chạy được trong môi trường đã thử. Bạn không cần thêm dependency chỉ để thực hiện hai ví dụ này. Khi dữ liệu đến từ JSON, hãy nhớ định dạng trao đổi và cấu trúc runtime là hai lựa chọn riêng; không mặc định đối tượng Set sẽ được serialize giống một mảng.
Về hiệu năng, tài liệu Set mô tả yêu cầu truy cập trung bình dưới tuyến tính theo số phần tử. Điều đó không phải lời hứa mọi engine luôn triển khai has bằng một thuật toán cụ thể hoặc mọi phép has đều nhanh hơn toàn bộ luồng dùng includes. Thời gian tạo cấu trúc và số lần tra cứu vẫn cần được tính vào bài toán.
Về chi phí vận hành trong codebase, một cấu trúc phụ cần thêm trách nhiệm invalidation và kiểm tra dữ liệu. Nếu lợi ích chưa được đo mà nhóm phải sửa nhiều bug đồng bộ, lựa chọn đó có thể chưa phù hợp. Nếu cùng một tập ổn định được tra cứu nhiều lần, Set là một ứng viên đáng thử bằng workload thật.
Kiểm tra trước khi chuyển đổi
Lập các ca có ID hợp lệ, ID không tồn tại, số và chuỗi trông giống nhau, phần tử trùng và dữ liệu được thêm hoặc xóa. Nếu dùng object, kiểm tra cả cùng tham chiếu và object mới cùng nội dung.
Khi đo hiệu năng, xác định bạn đo chỉ bước lookup hay cả tạo Set. Giữ số lượng dữ liệu, số lượt tìm, tỷ lệ hit/miss và môi trường chạy. Không lấy một lần đo nhỏ trong terminal để khẳng định lựa chọn đúng cho mọi màn hình hoặc request.
Lỗi thường gặp và kết luận
Lỗi phổ biến là tạo lại Set trong từng vòng lặp, giữ Set cũ sau khi mảng đổi hoặc vô tình loại phần tử trùng có ý nghĩa. Hãy chọn cấu trúc theo hợp đồng dữ liệu và vòng đời sử dụng trước, rồi dùng phép đo phù hợp để quyết định có cần tối ưu.
Hai cách đều hữu ích khi phục vụ đúng ý định. Một phép tra cứu rõ ràng, có dữ liệu nhất quán thường có giá trị hơn một thay đổi được gọi là “tối ưu” nhưng chưa được kiểm chứng ở cấp luồng xử lý.


