Một câu hỏi phỏng vấn thường bắt đầu bằng vài dòng code: sao chép object bằng spread, sửa một trường lồng nhau rồi hỏi object ban đầu còn nguyên không. Câu trả lời “spread tạo object mới” đúng nhưng chưa đủ để dự đoán chương trình.
Bài này đi từ kết quả cụ thể đến cách sửa và câu hỏi mở rộng. Ví dụ được kiểm tra bằng Node.js 24.18.1; tài liệu JavaScript được đối chiếu ngày 23/10/2026. Những kết quả này kiểm tra giá trị và tham chiếu, không phải benchmark hoặc kết quả render của React Native.
Dự đoán trước khi chạy
const user = { name: 'An', profile: { city: 'Hue' } };
const draft = { ...user };
console.log(draft === user);
console.log(draft.profile === user.profile);
draft.profile.city = 'Da Nang';
console.log(user.profile.city);
Kết quả theo thứ tự là false, true, rồi Da Nang. draft và user là hai object ngoài cùng khác nhau. Tuy nhiên, giá trị của thuộc tính profile trong cả hai vẫn trỏ tới cùng một object. Lệnh gán thành phố tác động tới object dùng chung ấy.
Khi giải thích, hãy chỉ rõ cấp đang được sao chép. Spread ở object tạo một bản sao nông của các thuộc tính own enumerable; nó không tự đi qua toàn bộ đồ thị dữ liệu và nhân bản mọi object lồng nhau. Đây là lý do việc object ngoài cùng khác nhau không chứng minh toàn bộ dữ liệu đã độc lập.
Sao chép dọc theo nhánh cần sửa
Nếu mục tiêu chỉ đổi thành phố và giữ dữ liệu ban đầu, tạo object mới cho nhánh profile rồi đưa nó vào object user mới:
import assert from 'node:assert/strict';
function withCity(user, city) {
return { ...user, profile: { ...user.profile, city } };
}
const original = { name: 'An', profile: { city: 'Hue' } };
const updated = withCity(original, 'Da Nang');
assert.equal(original.profile.city, 'Hue');
assert.equal(updated.profile.city, 'Da Nang');
assert.notEqual(updated, original);
assert.notEqual(updated.profile, original.profile);
Đây là cập nhật bất biến theo nhánh thay đổi, không phải deep clone toàn bộ. Nếu object có một thuộc tính khác không thay đổi, bạn có thể tiếp tục chia sẻ tham chiếu của phần đó khi hợp đồng dữ liệu cho phép. Cần biết nơi nào có quyền mutation để việc chia sẻ không làm hỏng giả định của bên đọc.
Ví dụ giả định profile tồn tại và có hình dạng đã biết. Với dữ liệu đầu vào có thể thiếu trường, hãy validate hoặc xác định quy tắc mặc định trước khi cập nhật. Đừng dùng spread để che mọi lỗi dữ liệu rồi vô tình tạo một object “có vẻ hợp lệ” nhưng thiếu thông tin bắt buộc.
Array cũng có câu hỏi tương tự
const rows = [{ id: 1, done: false }];
const copied = [...rows];
console.log(copied === rows);
console.log(copied[0] === rows[0]);
Hai dòng in ra false và true. Array mới vẫn chứa tham chiếu tới phần tử cũ. Nếu muốn đổi một phần tử theo ID, có thể dùng map và tạo object mới cho phần tử khớp, giữ nguyên các phần tử còn lại. Tạo array mới bằng spread rồi sửa object bên trong vẫn để lại vấn đề ban đầu.
Trong buổi phỏng vấn, hãy nói rõ điều kiện nhận diện phần tử, xử lý khi không tìm thấy ID và hành vi khi có ID trùng. Các câu hỏi đó cho thấy bạn đang suy luận về hợp đồng dữ liệu thay vì chỉ ghi nhớ một cách viết syntax.
Khi nào nghĩ đến structuredClone?
structuredClone hỗ trợ sao chép sâu các kiểu dữ liệu mà thuật toán structured clone chấp nhận, bao gồm một số cấu trúc mà cách chuyển qua JSON không giữ nguyên. Nó cũng có thể xử lý tham chiếu vòng. Tuy nhiên, không phải mọi giá trị đều sao chép được; ví dụ dữ liệu chứa function có thể gây DataCloneError.
Vì vậy, hãy xác định dữ liệu cần sao chép và các kiểu xuất hiện trong đó trước khi chọn công cụ. Với cập nhật state ở một nhánh đã biết, sao chép đúng nhánh thường diễn đạt ý định rõ hơn. Nếu mục tiêu là tạo snapshot độc lập của một cấu trúc phù hợp, hãy kiểm tra khả năng structured clone của runtime đang chạy.
Đừng dùng JSON.parse(JSON.stringify(value)) như một câu trả lời chung cho mọi object. Chuyển dữ liệu sang JSON là một phép chuyển biểu diễn với giới hạn riêng, không phải lời hứa giữ mọi kiểu dữ liệu và hành vi của object JavaScript.
Cách tự chấm câu trả lời
Trước tiên, dự đoán đúng các giá trị in ra. Tiếp theo, giải thích object ngoài cùng và object lồng nhau đang được chia sẻ như thế nào. Cuối cùng, đưa ra bản sửa và assertion chứng minh cả kết quả mới lẫn việc dữ liệu gốc không đổi.
Một câu trả lời tốt cũng phân biệt sao chép với deep equality. Hai object khác tham chiếu có thể chứa nội dung giống nhau; hai biểu thức đọc thuộc tính có thể trỏ tới cùng object. Nếu chỉ nhìn console hiển thị hai cấu trúc tương tự rồi suy ra identity, bạn dễ bỏ qua nguyên nhân của lỗi.
Kết luận
Spread giúp tạo container mới ở cấp bạn sử dụng nó. Để cập nhật dữ liệu lồng nhau an toàn, hãy chỉ ra những nhánh cần tạo mới và kiểm tra các tham chiếu liên quan. Cách giải thích đó có thể áp dụng cho cả bài phỏng vấn lẫn việc sửa lỗi mutation trong ứng dụng.





