Người dùng tắt thông báo nhưng lưu xong vẫn thấy thông báo bật. Một nguyên nhân dễ bỏ qua là code chỉ cập nhật khi if (input.enabled) đúng. Giá trị false bị coi như không gửi, dù đó chính là lựa chọn người dùng muốn lưu.
Với dữ liệu cập nhật một phần, hãy tách ba câu hỏi: trường có được cung cấp không, giá trị có đúng kiểu không và người gửi có được phép sửa không. Object.hasOwn() hỗ trợ câu hỏi đầu tiên; nó không trả lời thay hai câu còn lại.
1. Có thuộc tính không đồng nghĩa với giá trị truthy
Xét object được tạo trực tiếp trong JavaScript:
const input = {
enabled: false,
retries: 0,
note: '',
selected: undefined,
};
Object.hasOwn(input, 'enabled'); // true
Object.hasOwn(input, 'selected'); // true
Object.hasOwn(input, 'missing'); // false
Theo MDN Object.hasOwn(), phương thức kiểm tra thuộc tính riêng, kể cả khi giá trị là null hoặc undefined. Đây là điểm khác với phép kiểm tra truthiness.
Đọc input.selected === undefined không đủ để phân biệt thuộc tính tồn tại với giá trị undefined và thuộc tính không có. Trong thiết kế cập nhật form, hai trường hợp có thể mang ý nghĩa khác nhau nên cần contract rõ ràng.
2. So sánh với toán tử in
Toán tử in còn xét chuỗi prototype, được mô tả trong tài liệu MDN về in. Vì vậy hãy chọn công cụ theo câu hỏi nghiệp vụ:
const defaults = { enabled: true };
const input = Object.create(defaults);
input.note = '';
'enabled' in input; // true
Object.hasOwn(input, 'enabled'); // false
Object.hasOwn(input, 'note'); // true
Nếu đang xử lý phần cập nhật do caller cung cấp trực tiếp, giá trị kế thừa không nên tự trở thành yêu cầu thay đổi. Nếu đang kiểm tra một giao diện object có kế thừa theo thiết kế, in có thể là công cụ phù hợp. Không cần thay mọi chỗ dùng in bằng hasOwn.
3. Viết bộ cập nhật nhỏ với allowlist
Ví dụ dưới đây chỉ nhận hai trường đã được xác định. enabled phải là boolean; note phải là chuỗi không quá 200 đơn vị mã UTF-16 theo String.length. Đây là contract minh họa, không phải quy tắc đếm ký tự hiển thị cho mọi ngôn ngữ:
function readSettingsUpdate(input) {
if (input === null || typeof input !== 'object'
|| Array.isArray(input)) {
throw new TypeError('Expected an object');
}
const patch = {};
if (Object.hasOwn(input, 'enabled')) {
if (typeof input.enabled !== 'boolean') {
throw new TypeError('enabled must be boolean');
}
patch.enabled = input.enabled;
}
if (Object.hasOwn(input, 'note')) {
if (typeof input.note !== 'string' || input.note.length > 200) {
throw new TypeError('note must be a short string');
}
patch.note = input.note;
}
return patch;
}
Hàm giữ nguyên false và chuỗi rỗng, bỏ qua trường không nằm trong allowlist và từ chối null ở những trường không cho phép null. Nếu sản phẩm muốn null mang nghĩa “xóa giá trị”, hãy thêm nhánh validation có chủ đích thay vì vô tình chấp nhận.
Đây là bộ đọc dữ liệu, chưa ghi database. Backend vẫn phải xác thực, phân quyền và validation lại theo contract của API. Không copy toàn bộ input vào model chỉ vì đã kiểm tra một vài thuộc tính.
4. JSON và object trong bộ nhớ có khác biệt
Object JavaScript trong bộ nhớ có thể chứa undefined, nhưng JSON không có literal undefined. Ví dụ JSON.parse('{"enabled":false}') giữ trường enabled, còn JSON.parse('null') cho kết quả null. JSON hợp lệ không tự bảo đảm top-level là object; xem MDN JSON.parse().
Do đó, kiểm tra cấu trúc trước hasOwn là bước cần thiết khi caller có thể gửi array, null hoặc kiểu nguyên thủy. Nếu JSON không hợp lệ, lỗi parse cũng cần được xử lý tại biên nhận request, tách khỏi lỗi validation từng trường.
Với client dùng JSON.stringify, hãy kiểm tra payload trên đường truyền thay vì chỉ nhìn object trước khi serialize. Test xử lý undefined trong bộ nhớ không phải bằng chứng server sẽ nhận một trường undefined qua JSON.
5. Đừng gọi hasOwnProperty từ input một cách mù quáng
Object có thể tự chứa field hasOwnProperty, hoặc được tạo không có prototype. Object.hasOwn(input, key) tránh phụ thuộc vào method mang tên đó trên chính input. Khi hỗ trợ runtime cũ chưa có hasOwn, cần kiểm tra compatibility và chọn polyfill hoặc cách gọi chuẩn hóa phù hợp.
Phạm vi ví dụ là dữ liệu JSON và các object dữ liệu thông thường. Với object có getter hoặc Proxy do code khác tạo ra, việc truy cập thuộc tính có thể chạy logic khác; hasOwn không phải sandbox, bộ làm sạch dữ liệu hay cơ chế chống mọi dạng prototype pollution.
6. Kiểm thử theo ý nghĩa dữ liệu
| Đầu vào | Kết quả mong muốn của ví dụ |
|---|---|
{} | Không yêu cầu cập nhật trường nào |
{ enabled: false } | Giữ yêu cầu tắt thông báo |
{ note: '' } | Giữ yêu cầu xóa nội dung ghi chú |
{ enabled: 0 } | Từ chối sai kiểu |
{ note: null } | Từ chối theo contract không cho phép null |
null hoặc [] | Từ chối cấu trúc không hợp lệ |
Các ca kiểm tra trong bài đã được chạy bằng Node.js local: giá trị falsy hợp lệ được giữ, trường kế thừa không được dùng làm patch và đầu vào sai bị từ chối. Việc này xác minh ví dụ JavaScript, không thay thế kiểm thử form trên browser hay kiểm thử API tích hợp.
Khi review code cập nhật một phần, hãy hỏi “field có được gửi không?” trước “field có giá trị gì?”. Tách sự hiện diện, kiểu dữ liệu và quyền sửa giúp tránh những lỗi nhỏ nhưng ảnh hưởng trực tiếp đến lựa chọn của người dùng.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.