JSON is a format, not a copy of a JavaScript object
The examples here run in a modern browser console or a current Node.js environment. Both provide the global JSON object. JSON.stringify(value) produces JSON text; JSON.parse(text) reads JSON text and returns a JavaScript value. A useful first test is JSON.parse(JSON.stringify({ name: "Ada", active: true })), which returns an object with those two properties.
JSON can represent objects with string keys, arrays, strings, finite numbers, booleans, and null. It cannot directly represent undefined, functions, symbols, BigInt values, or object relationships such as shared references. JSON also has no syntax for comments or trailing commas. Its strings and object keys use double quotes, so {"name":"Ada"} is valid JSON text, while {'name':'Ada'} is not.
What JSON.stringify keeps, converts, and omits
For ordinary objects, JSON.stringify serializes enumerable own properties whose values it can represent. If an object property has the value undefined, a function, or a symbol, that property is omitted. For example, JSON.stringify({ ready: true, missing: undefined }) returns the text {"ready":true}. A property that is present with the value null is different: it remains in the output.
Arrays behave differently because their positions matter. An unsupported array element becomes null rather than disappearing: JSON.stringify([undefined, () => 1, Symbol("x")]) returns [null,null,null]. A direct call such as JSON.stringify(undefined) returns the JavaScript value undefined, not the string "undefined". Check the return value before passing it to an API that expects text.
Non-finite numbers also become null: JSON.stringify([NaN, Infinity]) returns [null,null]. JSON.stringify(12n), by contrast, normally throws a TypeError because JSON has no BigInt value. A Date commonly serializes as an ISO-style string through its toJSON method, but parsing that string does not recreate a Date. Class instances likewise do not retain their class behavior after parsing. Treat JSON as a data interchange format, not a general-purpose cloning mechanism.
Use a round trip to check your assumptions
When designing a value for storage or transfer, try a round trip with representative data: const text = JSON.stringify(value); const restored = JSON.parse(text);. Then inspect restored, not just text. This reveals changes that can be easy to miss, such as omitted properties, null array entries, and dates returned as strings. Only run JSON.parse if stringify actually returned a string.
If a value matters, decide how to encode it explicitly. For example, convert a BigInt to a decimal string before serializing, and define when that string should be converted back. For optional object properties, choose deliberately between omitting a property and storing null; consumers may interpret those states differently. A replacer function passed to JSON.stringify can customize serialization, but an explicit data shape is often easier to document and test.
Why JSON.parse fails
JSON.parse expects complete, valid JSON text. JSON.parse('{"count": 2}') succeeds; JSON.parse('{"count": 2,}') throws a SyntaxError because of the trailing comma. Other common causes include single-quoted strings, unquoted object keys, comments, an empty input, incomplete text, and JavaScript-only values such as undefined or NaN. Passing an object directly is not a substitute for providing JSON text.
Catch parse errors at boundaries where text may be invalid, such as user input, saved settings, or a network response. For example: let settings; try { settings = JSON.parse(input); } catch (error) { settings = null; console.error("Invalid JSON:", error); }. The exact error message can vary by JavaScript environment, so rely on the caught error rather than matching its wording. Parsing only checks JSON syntax; it does not confirm that settings has the properties or types your code needs.
Validate the result before using it
After parsing, check the shape your application expects. If settings should be an object with a boolean darkMode property, first reject null and arrays, then check typeof settings.darkMode === "boolean". This matters because every valid JSON value can appear at the top level: JSON.parse('null'), JSON.parse('42'), and JSON.parse('[]') all succeed, though none may fit your settings format.
Keep error handling close to the boundary and give the rest of your code a predictable value. In tests, cover a valid example, malformed text, a valid but unexpected type, and any serialization edge cases your application uses. That small set of checks makes JSON behavior explicit instead of leaving omissions and parse failures to surface later in the interface.



