Lớp `JSON` trong JavaScript
Bài viết này giải thích về lớp JSON trong JavaScript.
Chúng tôi sẽ giải thích về lớp JSON trong JavaScript với các ví dụ thực tế.
YouTube Video
Lớp JSON trong JavaScript
Đối tượng JSON chủ yếu có hai phương thức. JSON.stringify() chuyển đối tượng thành chuỗi JSON, còn JSON.parse() tạo đối tượng từ chuỗi JSON. JSON là định dạng trao đổi dữ liệu chỉ có thể biểu thị một số giá trị của JavaScript. Các hàm, undefined, Symbol và tham chiếu vòng (circular reference) không thể chuyển đổi trực tiếp sang JSON.
JSON.stringify()
Đầu tiên, đây là ví dụ cơ bản chuyển đối tượng thành chuỗi JSON.
1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
- Đoạn mã này là ví dụ đơn giản nhất về chuyển một đối tượng thông thường thành chuỗi JSON.
JSON.stringifytrả về chuỗi một cách đồng bộ.
Tham số thứ hai của JSON.stringify (replacer)
Bằng cách chỉ định replacer là tham số thứ hai của JSON.stringify, bạn có thể kiểm soát chi tiết quy tắc chuyển đổi. Đây là ví dụ sử dụng một mảng để chỉ giữ lại các thuộc tính cụ thể.
1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
- Trong ví dụ này,
passwordđược loại bỏ để đảm bảo đầu ra log an toàn. Điều này hữu ích khi muốn loại bỏ thông tin nhạy cảm vì lý do bảo mật.
Sử dụng replacer như một hàm (cho chuyển đổi và lọc giá trị)
Nếu muốn linh hoạt hơn, bạn có thể cung cấp một hàm để xử lý từng khóa và giá trị. Dưới đây là ví dụ giữ Date dưới dạng chuỗi ISO.
1// Use a replacer function to customize serialization.
2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
3const jsonCustom = JSON.stringify(data, (key, value) => {
4 // Convert Date objects to ISO strings explicitly
5 if (this && this[key] instanceof Date) {
6 return this[key].toISOString();
7 }
8 return value;
9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
- Trong
replacerdạng hàm, key và value sẽ được truyền vào, và giá trị trả về sẽ được dùng trong JSON cuối cùng.thistham chiếu đến đối tượng cha, nên cũng có thể dùng cho chuyển đổi lồng nhau.
Cơ bản về JSON.parse()
JSON.parse chuyển chuỗi JSON trở lại thành đối tượng. Bọc nó bằng try/catch để xử lý khi JSON không hợp lệ.
1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4 const parsed = JSON.parse(jsonText);
5 console.log(parsed.title); // Example
6} catch (err) {
7 console.error("Invalid JSON:", err.message);
8}- Nếu chuỗi không hợp lệ, sẽ ném ra ngoại lệ, vì vậy luôn cần xử lý ngoại lệ khi làm việc với nguồn dữ liệu bên ngoài.
Khôi phục tuỳ chỉnh bằng reviver (ví dụ khôi phục Date)
Bằng cách sử dụng tham số thứ hai reviver của JSON.parse, bạn có thể khôi phục giá trị. Đây là ví dụ chuyển một chuỗi ngày tháng đã lưu trở lại đối tượng Date.
1// Use a reviver to turn ISO date strings back into Date objects.
2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
3const obj = JSON.parse(jsonWithDate, (key, value) => {
4 // A simple check for ISO date-like strings
5 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
6 return new Date(value);
7 }
8 return value;
9});
10console.log(obj.joined instanceof Date); // true
reviversẽ được gọi cho từng khóa, và giá trị trả về sẽ được sử dụng trong đối tượng cuối cùng. Hãy kiểm tra định dạng ngày nghiêm ngặt tùy theo ứng dụng của bạn.
In đẹp (Pretty-print) và tham số space
Để xuất ra có thụt lề cho dễ đọc, truyền một số hoặc chuỗi làm tham số thứ ba cho JSON.stringify.
1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6 "host": "localhost",
7 "port": 3000,
8 "debug": true
9} */- Với JSON dành cho con người đọc chẳng hạn như log hoặc tệp cấu hình, việc khai báo thụt lề sẽ hữu ích. Tuy nhiên, hãy lưu ý điều này làm tăng kích thước.
Cách xử lý tham chiếu vòng lặp (circular reference)
JSON.stringify sẽ ném ra TypeError nếu có tham chiếu vòng. Một giải pháp phổ biến là tạo replacer sử dụng tập hợp seen để tránh tham chiếu vòng.
1// Safely stringify objects that may contain circular references.
2function safeStringify(value) {
3 const seen = new WeakSet();
4 return JSON.stringify(value, (key, val) => {
5 if (val && typeof val === "object") {
6 if (seen.has(val)) return "[Circular]";
7 seen.add(val);
8 }
9 return val;
10 });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
- Bằng cách dùng
WeakSet, bạn có thể phát hiện tham chiếu vòng mà không gây rò rỉ bộ nhớ. Nếu gặp tham chiếu vòng, nó sẽ trả về"[Circular]". Bạn cũng có thể gán một ID tham chiếu thay vì"[Circular]".
Tuỳ chỉnh chuyển đổi sang JSON với phương thức toJSON
Nếu bạn định nghĩa toJSON trên một đối tượng, JSON.stringify sẽ sử dụng giá trị trả về đó. Điều này hữu ích khi bạn muốn nhúng quy tắc chuyển đổi riêng cho mỗi kiểu.
1// Define toJSON on a class to customize its JSON representation.
2class Point {
3 constructor(x, y) {
4 this.x = x;
5 this.y = y;
6 }
7 toJSON() {
8 // This will be used by JSON.stringify
9 return { x: this.x, y: this.y, type: "Point" };
10 }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
toJSONcho phép định nghĩa quy tắc chuyển đổi tại cấp độ đối tượng, giúp nó trực quan và cục bộ hơn so với dùngreplacer.
Các hạn chế và lưu ý với JSON
Những ví dụ đáng chú ý không thể chuyển sang JSON là undefined, hàm, Symbol, BigInt và tham chiếu vòng. Hãy chú ý đến độ chính xác của số (như số nguyên lớn và làm tròn số thực theo IEEE).
1// Demonstrate values that can't be represented in JSON.
2const sample = {
3 a: undefined,
4 b: function () {},
5 c: Symbol("s"),
6 d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
7};
8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
9try {
10 console.log(JSON.stringify(sample));
11} catch (err) {
12 console.error("Error during stringify:", err.message);
13}- Khi xử lý
BigInt, hãy chuyển nó thành chuỗi hoặc định nghĩa biểu diễn riêng thông quareplacerhoặctoJSONtùy chỉnh.
Bảo mật (khi xử lý JSON không đáng tin cậy)
JSON.parse tự nó an toàn và được coi là an toàn hơn eval, nhưng việc tin tưởng và sử dụng trực tiếp đối tượng vừa parse được cho truy cập thuộc tính hoặc truy vấn cơ sở dữ liệu là nguy hiểm. Luôn xác thực và kiểm tra dữ liệu phù hợp với schema mong đợi.
1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5 console.log("Safe to use parsed.username and parsed.role.");
6} else {
7 throw new Error("Invalid payload");
8}- Đối với việc xác thực đầu vào (xác thực schema), sử dụng thư viện như
ajvsẽ rất mạnh mẽ.
Cân nhắc về hiệu năng
Việc tuần tự hóa và giải tuần tự hóa các đối tượng lớn thường xuyên sẽ tạo gánh nặng lên CPU và bộ nhớ. Nếu cần, bạn có thể cân nhắc gửi chỉ phần chênh lệch, dùng định dạng nhị phân (như MessagePack), hoặc truyền trực tuyến (xử lý dữ liệu tuần tự). Trong môi trường trình duyệt, bạn có thể tận dụng structuredClone (cho việc sao chép) hoặc các đối tượng có thể chuyển giao trong postMessage.
Một số lời khuyên cụ thể (ngắn gọn)
Bạn cũng có thể cân nhắc các điểm sau đây:.
- Đối với log, hãy tránh in đẹp và dùng JSON một dòng để giảm kích thước.
- Các đối tượng được tuần tự hóa thường xuyên nên được làm nhẹ trước đó, ví dụ bằng cách loại bỏ các thuộc tính không cần thiết.
- Hãy sử dụng
replacerđể giảm thiểu việc duyệt và loại trừ các thuộc tính không cần thiết.
Ví dụ thực tế: quy trình gửi/nhận yêu cầu API
Cuối cùng, đây là chuỗi thao tác cho thấy cách chuyển đổi một đối tượng sang JSON an toàn để gửi tới máy chủ và khôi phục lại ngày tháng từ phản hồi nhận về.
1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
2async function sendUserUpdate(url, user) {
3 // Remove sensitive info before sending
4 const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
5 const res = await fetch(url, {
6 method: "POST",
7 headers: { "Content-Type": "application/json" },
8 body: payload
9 });
10
11 const text = await res.text();
12 // Reviver: convert ISO date strings back to Date
13 const data = JSON.parse(text, (key, value) => {
14 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15 return new Date(value);
16 }
17 return value;
18 });
19 return data;
20}- Trong ví dụ này,
passwordđược loại bỏ trước khi gửi và ngày tháng được khôi phục bằngreviversau khi nhận. Trong hoạt động thực tế, bạn cần bổ sung xử lý lỗi và timeout.
Tóm tắt
Sử dụng JSON.stringify() và JSON.parse(), bạn có thể chuyển đổi qua lại giữa đối tượng và chuỗi. Bạn có thể tuỳ chỉnh quá trình chuyển đổi bằng replacer và reviver, cũng như kiểm soát từng đối tượng riêng biệt khi triển khai toJSON trong một class. Các tham chiếu vòng, BigInt, hàm, v.v. không thể xử lý trực tiếp mà cần phải xử lý trước.
Luôn xác thực dữ liệu đầu vào bên ngoài, và đảm bảo thông tin nhạy cảm không xuất hiện trong log hoặc khi truyền đi. Nếu kích thước dữ liệu lớn, nên tránh in đẹp, cân nhắc truyền phần chênh lệch hoặc sử dụng định dạng nhị phân.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.