Lập trình · 21/10/2026

Object.assign trong JavaScript: lệnh lỗi nhưng object vẫn bị sửa một phần

Object.assign ném TypeError nhưng một số thuộc tính trên object đích đã đổi. Phép gán này không tự hoàn tác những bước đã thành công trước lỗi. Khi dùng để cập nhật cấu hình hoặc state, cần hiểu quá trình đọc nguồn, ghi đích và thời điểm công bố kết quả.

Object.assign trong JavaScript: lệnh lỗi nhưng object vẫn bị sửa một phần

Object.assign ném TypeError nhưng một số thuộc tính trên object đích đã đổi. Phép gán này không tự hoàn tác những bước đã thành công trước lỗi. Khi dùng để cập nhật cấu hình hoặc state, cần hiểu quá trình đọc nguồn, ghi đích và thời điểm công bố kết quả.

1. Lỗi không đồng nghĩa chưa có thay đổi

const target = { first: 0 };
Object.defineProperty(target, 'locked', { value: 0, writable: false });
try {
  Object.assign(target, { first: 1, locked: 2, last: 3 });
} catch (error) {
  console.log(error instanceof TypeError); // true
}
console.log(target.first); // 1
console.log(target.locked); // 0
console.log(Object.hasOwn(target, 'last')); // false

Trong ví dụ, first được ghi trước khi thao tác gặp locked không cho phép ghi. last chưa được xử lý. Nếu catch chỉ ghi log rồi tiếp tục dùng target như thể còn nguyên, ứng dụng có thể giữ trạng thái cập nhật dở.

2. Ghi vào đích có thể gọi setter

const writes = [];
const target = {
  set mode(value) { writes.push(value); }
};
Object.assign(target, { mode: 'dark' });
console.log(writes.join(',')); // dark
const copy = { ...target, mode: 'light' };
console.log(writes.join(',')); // dark
console.log(copy.mode); // light

assign dùng thao tác ghi thuộc tính trên target nên setter có thể chạy. Object spread tạo thuộc tính dữ liệu trên object mới, không gọi setter mode của target để ghi light. Hai cách viết không hoàn toàn tương đương khi object có accessor.

3. Đọc nguồn có thể gọi getter

let reads = 0;
const source = {
  get count() { reads += 1; return 7; }
};
const assigned = Object.assign({}, source);
const spread = { ...source };
console.log(reads); // 2
console.log(assigned.count); // 7
console.log(typeof Object.getOwnPropertyDescriptor(assigned, 'count').get); // undefined
console.log(spread.count); // 7

Cả assign và spread đều đọc getter trong ví dụ. Kết quả chứa giá trị 7 đã đọc, không tự giữ nguyên getter của source. Nếu mục tiêu là sao chép descriptor hoặc hành vi của một instance, phép sao chép giá trị nông có thể không đúng yêu cầu.

Getter có thể ném lỗi hoặc tạo tác dụng phụ. Vì vậy, đổi assign sang spread không bảo đảm quá trình trở thành thuần túy hay không thể thất bại.

4. Tạo ứng viên mới rồi mới thay tham chiếu state

let state = { mode: 'light' };
const previous = state;
let reads = 0;
const patch = {
  get mode() { reads += 1; throw new Error('read failed'); }
};
try {
  state = { ...state, ...patch };
} catch (error) {
  console.log(error.message); // read failed
}
console.log(state === previous); // true
console.log(state.mode); // light
console.log(reads); // 1

Vế phải chưa tạo xong nên phép gán lại state chưa xảy ra. Object cũ được giữ nguyên trong ví dụ này. Với dữ liệu thực tế, xây dựng ứng viên, kiểm tra schema, rồi mới công bố state mới theo cách phù hợp với framework.

Đây không phải giao dịch hoàn tác mọi thứ: getter vẫn đã chạy một lần. Object lồng được sao chép nông vẫn có thể dùng chung tham chiếu, và tác dụng phụ bên ngoài không tự quay lại. Cách này phù hợp dữ liệu ứng dụng thông thường; không nên áp dụng máy móc lên class instance hoặc object có hành vi đặc biệt.

5. Kiểm thử cả trạng thái sau lỗi

  • Xác nhận thuộc tính nào đã đổi khi assign thất bại.
  • Kiểm tra getter và setter có được gọi không.
  • Phân biệt sao chép giá trị với sao chép descriptor.
  • Kiểm tra tham chiếu state cũ khi tạo ứng viên thất bại.
  • Đặt validation trước khi công bố kết quả mới.

Tám khối ví dụ Việt–Anh đã chạy bằng Node.js, kèm kiểm tra ghi dở và descriptor. Chưa kiểm thử tích hợp framework hoặc hiệu năng. Tham khảo MDN Object.assign và MDN Spread syntax.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

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