Why JSON.stringify calls inherited getters with a replacer array
A JSON.stringify replacer array supplies the same property names to the outer object and any nested objects. In this checkout payload, including "status" for the outer object also calls an inherited getter named status on the nested quote.
class Quote {
id = "q-7";
get status() {
console.log("Reading quote.status");
return "needs-review";
}
}
const checkout = {
status: "submitted",
quote: new Quote(),
note: "Local draft",
};
console.log(JSON.stringify(checkout));
console.log(JSON.stringify(checkout, ["id", "status", "quote"]));
{"status":"submitted","quote":{"id":"q-7"},"note":"Local draft"}
Reading quote.status
{"status":"submitted","quote":{"id":"q-7","status":"needs-review"}}
The replacer drops the local note and adds quote.status. The log shows that serialization ran the getter to obtain the value.
How the array changes what gets read
A getter is a function JavaScript calls during a property read, such as checkout.quote.status. This getter lives on Quote.prototype, the shared object that quote instances inherit from. The quote instance has its own id field and inherits status. The first JSON.stringify call uses each object’s own enumerable properties, the same set of names that Object.keys lists. On the quote, that gives it only id. MDN’s guide to getters · ECMAScript’s object serialization rules
The array in the second call is called a replacer. It supplies one list of property names that JSON.stringify tries on every non-array object it reaches. When it gets to checkout.quote, it tries to read id, status, and quote there too. The read for status finds the getter on Quote.prototype and calls it. The read for quote on the quote instance produces undefined, so that property is omitted. ECMAScript’s property serialization rules · ECMAScript’s property lookup rules
Removing "status" from the array prevents the getter call and removes both status fields. The result is {"quote":{"id":"q-7"}}, which omits the checkout status the API requires.
A request body with different keys at each level
A plain object can represent the exact request shape. The checkout contributes its status, and the nested quote contributes only its id.
const body = JSON.stringify({
status: checkout.status,
quote: {
id: checkout.quote.id,
},
});
console.log(body);
// {"status":"submitted","quote":{"id":"q-7"}}
A replacer array is concise for a flat plain object with known keys. For a nested request body, an explicit object lets each level use different keys. A test with a real quote instance checks that the nested getter stays outside the payload.
The examples were run in Node.js 24.13.0.
For more on how JavaScript finds inherited properties, see JavaScript Classes Under the Hood.