My first week at a job, I pushed config that broke staging. I had written:

const config = {
  database: {
    host: 'localhost',
    port: 5432,
    onConnect: () => console.log('connected'),
  }
};

// Later, the config was JSON-serialized and sent to a microservice:
fs.writeFileSync('config.json', JSON.stringify(config));

Everything looked fine until I read config.json:

{"database":{"host":"localhost","port":5432}}

Where did onConnect go? Where were my single quotes? And why was port not decimal?

That was the day I truly understood that JSON is not JavaScript. They're syntactically related but semantically different. Here's what I've learned since.

The Quote Wars: Single vs Double

In JavaScript, string literals can use single quotes, double quotes, or backticks — all equally valid:

const js = {
  name: 'Alice',
  greeting: "Hello",
  template: `Hi, ${name}`
};

In JSON, all strings must use double quotes. No exceptions.

{
  "name": "Alice",
  "greeting": "Hello"
}
// INVALID JSON:
{'name': 'Alice'}
{"name": 'Alice'}

Why this matters: If you're writing JSON by hand and reach for single quotes out of habit (I do this constantly), a parser will reject your file. The error message Unexpected token ' is almost always single quotes in a JSON context.

Key Naming: Quoted vs Unquoted

JavaScript object keys can be unquoted identifiers (if they're valid identifiers):

const person = {
  name: 'Alice',
  age: 30,
  'favorite-color': 'blue' // needs quotes because of hyphen
};

JSON requires all keys to be double-quoted:

{
  "name": "Alice",
  "age": 30,
  "favorite-color": "blue"
}

This trips up developers who copy-paste a JS object into a JSON file without thinking about key quoting. If you're migrating a JavaScript config file to JSON, run it through a validator like the JSON Formatter — it catches unquoted keys instantly.

Functions: The Silent Disappearance

This is the one that bit me on day one. JavaScript objects can hold functions:

const server = {
  name: 'api-server',
  start: function() {
    console.log('Starting...');
  },
  stop() {
    console.log('Stopping...');
  }
};

JSON has no representation for functions. When you serialize:

JSON.stringify(server);
// {"name":"api-server"}
// start and stop — gone. No error. No warning. Just gone.

This creates hard-to-find bugs because the JavaScript code works perfectly, but after serialization/deserialization, your object is missing behavior. The function reference survives in-memory, but once it crosses a JSON boundary (file, HTTP response, WebSocket), it evaporates.

Practical advice: Never rely on functions surviving a JSON round-trip. If you need to serialize behavior, use a registry pattern or embed a string identifier that the consumer maps to an actual function.

Undefined vs Null: The JSON Filter

JavaScript distinguishes between undefined and null. JSON only has null:

const obj = {
  a: undefined,
  b: null,
  c: 42
};

JSON.stringify(obj);
// {"b":null,"c":42}

Notice the key a was removed entirely. JSON.stringify drops keys with undefined values. null values are preserved.

This is a common source of schema validation bugs. If you're building an API that expects certain fields, but those fields are set to undefined in your JavaScript object, they'll silently disappear from the JSON payload.

// Bad — field disappears
const payload = {
  name: 'Alice',
  address: undefined // API expected this field, but it's gone
};
console.log(JSON.stringify(payload));
// {"name":"Alice"}

// Good — field is present with null
const better = {
  name: 'Alice',
  address: null
};
console.log(JSON.stringify(better));
// {"name":"Alice","address":null}

Comments: Yes vs No

JavaScript object literals support comments:

const config = {
  // This is the server port
  port: 3000,
  /*
   * Enable this in production only
   */
  debug: false
};

JSON does not support comments of any kind:

{
  // "port": 3000,
  /* "debug": false */
}

Both of these will cause parse errors in JSON. If you need to annotate JSON data, a common convention is to use a "_comment" key:

{
  "_comment": "Set this to the production port",
  "port": 3000,
  "_warning": "Enable debug mode only during development"
}

Some tooling and configuration ecosystems (like VSCode's settings.json or TypeScript's tsconfig.json) relax this rule, but those are using a custom parser, not JSON.parse().

Trailing Commas: Allowed vs Forbidden

JavaScript allows trailing commas since ES2017:

const team = {
  lead: 'Alice',
  engineer: 'Bob',
  intern: 'Charlie', // OK
};

JSON rejects them:

{
  "lead": "Alice",
  "engineer": "Bob",
  "intern": "Charlie"
}

Numbers: The Leading Zero and NaN Cases

JavaScript numbers can have leading zeros in some contexts (octal before strict mode), NaN, Infinity, and -Infinity:

const nums = {
  decimal: 42,
  octal: 0o52,  // valid JS, valid JSON? No.
  nan: NaN,
  inf: Infinity
};

JSON.stringify(nums);
// {"decimal":42,"nan":null,"inf":null}

NaN and Infinity become null in JSON. JSON numbers don't support NaN, Infinity, or leading zeros.

Date Handling: String or Not?

JavaScript Date objects serialize to strings in JSON:

const event = { createdAt: new Date('2026-06-04') };
JSON.stringify(event);
// {"createdAt":"2026-06-04T00:00:00.000Z"}

But there's no automatic deserialization:

const parsed = JSON.parse('{"createdAt":"2026-06-04T00:00:00.000Z"}');
console.log(typeof parsed.createdAt); // "string", not "object" (Date)

If you need to recover the Date object, you have to parse it manually or use a reviver function:

const data = JSON.parse(raw, (key, value) => {
  if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value);
  }
  return value;
});

Summary Table

FeatureJavaScript Object LiteralJSON
String quotesSingle, double, or backtickDouble only
Key quotesOptional (if valid identifier)Required (double quotes)
Functions✅ Yes❌ No
undefined✅ Valid❌ Not supported
null✅ Valid✅ Valid
Comments✅ Yes❌ No
Trailing commas✅ Yes (ES2017+)❌ No
NaN / Infinity✅ Valid❌ Not supported
Date objects✅ NativeSerialized as string

The JSON Formatter can help you validate whether your data is actually JSON or just looks like JavaScript — paste it in, and it'll tell you instantly if there are any syntax issues.


FAQ

Q: Can I use JSON.parse on a JavaScript object literal?

A: Only if it's valid JSON syntax. Object literals with single quotes, functions, or trailing commas will throw. Use eval() (not recommended) or a custom parser if you need to parse JS-like objects.

Q: Why does JSON.stringify remove my functions silently?

A: By design. JSON has no function type. JSON.stringify drops any property whose value is a function, symbol, or undefined.

Q: How do I handle BigInt in JSON?

A: JSON.stringify throws on BigInt by default. You'll need a custom replacer that converts BigInt to a string or number.

Q: Can JSON contain circular references?

A: No. JSON.stringify throws "Converting circular structure to JSON" if your object has circular references. Use a library like flatted for circular data.

Q: Is there a way to make JSON.parse handle dates automatically?

A: Use the reviver parameter of JSON.parse to detect date-like strings and convert them. But be careful — not every ISO string is a date.

Q: What's JSON5 and should I use it?

A: JSON5 is a superset of JSON that allows comments, trailing commas, single quotes, and unquoted keys. Useful for config files, but not for data interchange because other languages may not support it.

Q: Why does console.log sometimes show a different format than my JSON file?

A: The browser console formats objects differently for readability. It's not showing you the raw JSON — it's showing you the parsed JavaScript object representation.

Q: Can I use Map or Set in JSON?

A: Not directly. JSON.stringify converts Map and Set to empty objects by default. You need to convert them to arrays before serialization.


Next time you're tempted to use a JavaScript object literal as config or data interchange, double-check the syntax. Or paste it into the JSON Formatter first — one click and you'll know whether it's valid JSON or just JavaScript-looking data.