JSON.parse(JSON.stringify(value)) thường xuất hiện như một mẹo sao chép dữ liệu lồng nhau. Nó có thể đáp ứng một object chỉ gồm dữ liệu JSON, nhưng cũng có thể đổi Date thành chuỗi và làm mất thuộc tính undefined. Bản sao không còn cùng ý nghĩa dù vẫn có vẻ đúng khi nhìn nhanh.
Bài đối chiếu MDN ngày 28/10/2026 và chạy fixture tự tạo trên Node.js 24.18.1. So sánh tập trung vào kiểu dữ liệu, cách bắt đầu, mức kiểm soát và chi phí tích hợp. Bài không benchmark thời gian hoặc bộ nhớ, không tuyên bố một cách luôn nhanh hơn.
1. Bắt đầu từ một object có kiểu dữ liệu cụ thể
const source = {
profile: {name: 'An'},
createdAt: new Date('2026-10-27T08:00:00Z'),
optional: undefined,
score: NaN,
};
const jsonCopy = JSON.parse(JSON.stringify(source));
const cloneCopy = structuredClone(source);
console.log(typeof jsonCopy.createdAt, cloneCopy.createdAt instanceof Date);
console.log('optional' in jsonCopy, 'optional' in cloneCopy);
console.log(jsonCopy.score, Number.isNaN(cloneCopy.score));
cloneCopy.profile.name = 'Bình';
console.log(source.profile.name);
Các dòng lần lượt cho string true, false true, null true, rồi An. Structured clone giữ những kiểu được hỗ trợ trong fixture và tạo profile riêng. JSON round-trip thực hiện chuyển đổi qua biểu diễn JSON, nên kết quả phản ánh quy tắc serialize của JSON.
Đây không phải lý do để loại JSON khỏi hệ thống. Nếu yêu cầu là chuẩn bị dữ liệu để truyền qua một hợp đồng JSON, việc chuyển Date thành chuỗi có thể là chủ ý. Nhưng nếu mục tiêu là giữ Date để tiếp tục gọi phương thức của nó trong bộ nhớ, hãy kiểm tra kiểu sau khi sao chép.
2. Vòng tham chiếu là một phép thử khác
const circular = {name: 'demo'};
circular.self = circular;
try {
JSON.stringify(circular);
} catch (error) {
console.log(error.name);
}
const circularCopy = structuredClone(circular);
console.log(circularCopy !== circular);
console.log(circularCopy.self === circularCopy);
Kết quả là TypeError, true, true. Bản clone giữ cấu trúc vòng của dữ liệu mới, không trỏ ngược vào object gốc. Trong hệ thống dùng JSON cho mạng hoặc lưu trữ, sự tồn tại của vòng tham chiếu còn cho thấy bạn cần thiết kế lại dữ liệu đầu ra, thay vì chỉ đổi hàm clone rồi tiếp tục stringify nó.
3. Không có một hàm sao chép mọi thứ
try {
structuredClone({run: () => 1});
} catch (error) {
console.log(error.name);
}
console.log(JSON.stringify({run: () => 1}));
Ở runtime kiểm tra, kết quả là DataCloneError rồi {}. Một bên từ chối hàm không được hỗ trợ; bên kia bỏ thuộc tính đó khi serialize object. Không bên nào tạo ra một object mới giữ nguyên phương thức run.
Structured clone cũng không phải công cụ bảo toàn tùy ý prototype, property descriptor hoặc mọi hành vi của class tự viết. Nếu dữ liệu có những ràng buộc đó, hãy thiết kế phương thức chuyển đổi riêng và kiểm tra kết quả theo hợp đồng của kiểu dữ liệu.
4. So sánh cùng một nhóm tiêu chí
| Tiêu chí | structuredClone | JSON round-trip |
|---|---|---|
| Bắt đầu | Một lời gọi ở runtime hỗ trợ | Hai bước stringify rồi parse |
| Kiểu dữ liệu | Giữ nhiều kiểu được thuật toán hỗ trợ | Theo tập kiểu và quy tắc JSON |
| Lỗi khó thấy | Có thể ném lỗi với dữ liệu không clone được | Có thể bỏ hoặc đổi giá trị trước khi parse |
| Hệ sinh thái | Cần kiểm tra runtime mục tiêu | Phổ biến trong luồng dữ liệu JSON |
| Hiệu năng | Chưa đo trong bài | Chưa đo trong bài |
| Chi phí vận hành | Không có dịch vụ ngoài trong fixture | Không có dịch vụ ngoài trong fixture |
Với dữ liệu trong bộ nhớ cần giữ Date và vòng tham chiếu như ví dụ, structuredClone phù hợp hơn. Với một DTO đã được định nghĩa chỉ gồm kiểu JSON và cần serialize, hãy ưu tiên hợp đồng DTO rõ ràng. Đừng thêm stringify/parse chỉ để gọi đó là deep clone nếu mục tiêu thực sự là cập nhật vài trường trong state.
Kiểm tra trước khi thay trong dự án
Liệt kê các kiểu thực tế: array, object lồng, Date, Map, BigInt hoặc class tự viết. Sau đó kiểm tra thuộc tính còn tồn tại, kiểu trả về và quan hệ tham chiếu cần giữ. Chỉ dùng console.log của toàn object có thể làm bạn bỏ sót khác biệt giữa chuỗi ngày và Date.
Ví dụ không dùng tùy chọn transfer. Nếu ứng dụng có buffer và muốn chuyển quyền sở hữu, cần đọc hành vi transfer riêng; nó có ảnh hưởng khác với một bản sao thông thường.
Các ca trên đã chạy trên Node, chưa kiểm tra từng trình duyệt hoặc engine JavaScript của app mobile. Trước khi đưa vào React Native hay một runtime cũ, xác nhận API có sẵn và chạy lại fixture ở môi trường đó. Chọn dựa trên dữ liệu cần giữ và giới hạn đã kiểm tra, không chỉ dựa vào độ ngắn của một dòng code.



