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

JavaScript Array.push trả độ dài: tránh biến mảng thành số khi cập nhật dữ liệu

Gán rows = rows.push(item) làm biến rows nhận một số, trong khi mảng cũ đã bị sửa. Lỗi có thể chỉ lộ ra ở thao tác tiếp theo khi code gọi map hoặc push trên biến không còn là mảng. Tách giá trị trả về khỏi tác động lên dữ liệu giúp tránh nhầm lẫn này.

JavaScript Array.push trả độ dài: tránh biến mảng thành số khi cập nhật dữ liệu

Gán rows = rows.push(item) làm biến rows nhận một số, trong khi mảng cũ đã bị sửa. Lỗi có thể chỉ lộ ra ở thao tác tiếp theo khi code gọi map hoặc push trên biến không còn là mảng. Tách giá trị trả về khỏi tác động lên dữ liệu giúp tránh nhầm lẫn này.

1. push sửa mảng và trả độ dài mới

let rows = ['A'];
const alias = rows;
rows = rows.push('B');
console.log(rows); // 2
console.log(Array.isArray(rows)); // false
console.log(JSON.stringify(alias)); // ["A","B"]

MDN Array.push mô tả hai hành vi riêng: thêm phần tử vào cuối mảng và trả length mới. alias vẫn trỏ tới mảng đã được thêm 'B'; chỉ biến rows bị gán sang số 2. Đây không phải một phép tạo mảng mới.

Nếu muốn sửa trực tiếp mảng, gọi rows.push(item) như một câu lệnh riêng. Nếu cần số phần tử, đặt tên kết quả như newLength. Còn hàm đã hứa trả mảng không nên return rows.push(item), vì bên gọi sẽ nhận sai kiểu.

2. Callback reduce cũng có thể trả nhầm số

try {
  [1, 2].reduce((acc, value) => acc.push(value), []);
} catch (error) {
  console.log(error instanceof TypeError); // true
}
const collected = [1, 2].reduce((acc, value) => {
  acc.push(value);
  return acc;
}, []);
console.log(JSON.stringify(collected)); // [1,2]

Ở callback đầu tiên của phiên bản lỗi, push trả 1. Giá trị đó trở thành accumulator của lượt tiếp theo, rồi việc gọi acc.push thất bại. Phiên bản sửa trả lại mảng acc sau khi thêm phần tử. Ví dụ dùng accumulator mới tạo từ [], không phải mảng đầu vào.

Với chỉ một phần tử, phiên bản lỗi chưa có lượt thứ hai để ném exception nhưng vẫn trả sai kiểu. Với đầu vào rỗng, nó lại trả mảng khởi tạo. Vì vậy, test chỉ mảng rỗng hoặc chỉ kiểm tra có lỗi hay không có thể bỏ sót contract trả về không nhất quán.

3. Trả mảng mới khi không muốn sửa nguồn

function appendItem(rows, item) {
  if (!Array.isArray(rows)) {
    throw new TypeError('rows must be an array');
  }
  return [...rows, item];
}
const original = ['A'];
const next = appendItem(original, 'B');
console.log(JSON.stringify(original)); // ["A"]
console.log(JSON.stringify(next)); // ["A","B"]
console.log(next === original); // false

Helper dùng array spread để tạo mảng ngoài mới. Contract dành cho mảng dữ liệu thông thường với iterator chuẩn; item được thêm như đúng một phần tử, kể cả khi item là một mảng. Helper không xác thực schema hoặc loại phần tử trùng.

Đây là sao chép nông: các phần tử object cũ vẫn được chia sẻ với nguồn, và object item cũng được lưu bằng tham chiếu. Nếu đầu vào là mảng thưa, spread tạo các phần tử undefined tại ô trống. Khi cần giữ cấu trúc thưa hoặc tách bản ghi lồng nhau, chọn chính sách riêng thay vì xem helper là bản sao độc lập hoàn toàn.

4. Chọn kiểu trả về theo bên gọi

Một hàm cập nhật trực tiếp có thể trả độ dài, trả mảng hoặc không trả gì, miễn là contract rõ ràng. Lỗi xảy ra khi bên gọi dựa vào tên “thêm phần tử” để đoán kiểu trả về. Trong luồng cập nhật state cần mảng mới, truyền kết quả push sẽ đưa một số vào vị trí mong đợi mảng.

Không cần dùng reduce để sao chép nguyên mảng như ví dụ minh họa. Trong tác vụ thực tế, chọn vòng lặp hoặc phương thức thể hiện đúng mục đích. Khi thêm nhiều phần tử, làm rõ đang thêm một mảng con hay từng phần tử; đừng sửa thêm cú pháp spread mà chưa kiểm tra schema đầu vào.

5. Checklist khi review thao tác thêm dữ liệu

  • Phương thức sửa mảng hiện có hay tạo mảng mới?
  • Biến nhận kết quả cần number hay array?
  • Callback reduce có trả đúng accumulator sau mỗi lượt?
  • Đã thử mảng rỗng, một phần tử và ít nhất hai phần tử?
  • Các tham chiếu khác có được phép quan sát thay đổi nguồn?

Sáu khối mã Việt–Anh đã chạy bằng Node.js; kiểm tra bổ sung đầu vào một phần tử của reduce, sai kiểu đầu vào và tham chiếu object của helper. Chưa tích hợp thư viện quản lý state hoặc giao diện thực tế.

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.