JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeQuestionsjavascript
PrevNext

Learn the concept

Objects & Copying

javascript
senior
json

How would you implement JSON.stringify from scratch?

json
stringify
polyfill
implementation
recursion
interview-coding
Quick Answer

A JSON.stringify polyfill recursively converts JavaScript values to JSON strings by handling each type differently: strings get quoted, numbers and booleans become literals, arrays and objects are recursively stringified, and special values like undefined, functions, and Symbols are either omitted or converted to null.

Detailed Explanation

Implementing JSON.stringify from scratch is a common interview question at companies like Swiggy. It tests understanding of JavaScript type system, recursion, and edge case handling.

How JSON.stringify Works

JSON.stringify(value, replacer?, space?) converts a value to a JSON string:

Type Handling Rules: | JavaScript Type | JSON Output | |---|---| | string | Quoted string with escaped special chars | | number (finite) | Number literal | | NaN, Infinity | null | | boolean | true or false | | null | null | | undefined | Omitted in objects, null in arrays | | function | Omitted in objects, null in arrays | | Symbol | Omitted in objects, null in arrays | | BigInt | Throws TypeError | | Date | Calls .toISOString() (via toJSON) | | Array | [...] with elements stringified | | Object | {...} with enumerable own properties |

Special Behaviors:

  • Objects with a toJSON() method: call it and stringify the return value
  • Circular references: throw a TypeError
  • The replacer parameter can be a function (transforms values) or an array (whitelist of keys)
  • The space parameter controls indentation (number of spaces or a string prefix)

Implementation Strategy

  1. Handle primitives first (string, number, boolean, null)
  2. Handle special values (undefined, functions, Symbols, NaN, Infinity, BigInt)
  3. Check for toJSON() method
  4. Handle arrays (map elements, replace undefined/functions with null)
  5. Handle objects (iterate enumerable own properties, skip undefined/function/Symbol values)
  6. Detect circular references using a Set or WeakSet
  7. Apply string escaping for special characters

Code Examples

Core JSON.stringify implementationJavaScript
function myStringify(value, replacer, space) {
  const seen = new WeakSet(); // Circular reference detection

  function stringify(val, key, holder) {
    // Apply replacer function
    if (typeof replacer === 'function') {
      val = replacer.call(holder, key, val);
    }

    // Handle toJSON method
    if (val !== null && typeof val === 'object' && typeof val.toJSON === 'function') {
      val = val.toJSON(key);
    }

    // Primitives
    if (val === null) return 'null';
    if (val === true) return 'true';
    if (val === false) return 'false';
    if (typeof val === 'string') return escapeString(val);

    if (typeof val === 'number') {
      return Number.isFinite(val) ? String(val) : 'null';
    }

    if (typeof val === 'bigint') {
      throw new TypeError('BigInt value can\'t be serialized in JSON');
    }

    // undefined, functions, and Symbols return undefined
    if (typeof val === 'undefined' || typeof val === 'function' || typeof val === 'symbol') {
      return undefined;
    }

    // Objects and arrays
    if (typeof val === 'object') {
      // Circular reference check
      if (seen.has(val)) {
        throw new TypeError('Converting circular structure to JSON');
      }
      seen.add(val);

      let result;
      if (Array.isArray(val)) {
        result = stringifyArray(val);
      } else {
        result = stringifyObject(val);
      }

      seen.delete(val);
      return result;
    }

    return undefined;
  }

  function stringifyArray(arr) {
    const items = arr.map((item, i) => {
      const val = stringify(item, String(i), arr);
      return val === undefined ? 'null' : val;
    });
    return `[${items.join(',')}]`;
  }

  function stringifyObject(obj) {
    const keys = typeof replacer === 'object' && Array.isArray(replacer)
      ? replacer.filter(k => typeof k === 'string' || typeof k === 'number').map(String)
      : Object.keys(obj);

    const pairs = [];
    for (const k of keys) {
      const val = stringify(obj[k], k, obj);
      if (val !== undefined) {
        pairs.push(`${escapeString(k)}:${val}`);
      }
    }
    return `{${pairs.join(',')}}`;
  }

  function escapeString(str) {
    return '"' + str
      .replace(/\\/g, '\\\\')
      .replace(/"/g, '\\"')
      .replace(/\n/g, '\\n')
      .replace(/\r/g, '\\r')
      .replace(/\t/g, '\\t')
      .replace(/[\b]/g, '\\b')
      .replace(/\f/g, '\\f') + '"';
  }

  return stringify(value, '', { '': value });
}

Real-World Applications

Use Cases

Custom Serialization for Logging

Building logging utilities that need to serialize objects with custom handling for sensitive data (redacting passwords), circular references (Node.js util.inspect), and non-JSON types (BigInt, Map, Set).

Deep Clone via Serialization

Understanding JSON.stringify limitations helps explain why structuredClone is preferred for deep cloning — JSON.stringify drops undefined, functions, Symbols, and fails on circular references.

Mini Projects

Full JSON.stringify with Indentation

advanced

Extend the implementation to support the space parameter for pretty-printing with proper indentation at each nesting level.

Industry Examples

Douglas Crockford

Created the original JSON specification and reference implementations including json2.js which polyfilled JSON.stringify for older browsers.

Resources

MDN - JSON.stringify()

docs

ECMA-262 - JSON.stringify specification

docs

Related Questions

What are polyfills and how do you implement Array.prototype.map from scratch?

junior
polyfills

What are Proxies in JavaScript and how can they be used?

senior
metaprogramming
Previous
How would you implement Function.prototype.bind from scratch?
Next
How would you implement Promise.all and Promise.race from scratch?
PrevNext