skip to content

In JavaScript, how do `'toString' in obj` and `Object.hasOwn(obj, 'toString')` differ for a plain object literal, and when does that difference matter?

level: juniorimportance: must knowfreq 58%

answer

  1. one walks the chain, one does not
  2. inherited names are still reachable names
  3. data versus capability
  4. beware objects with no prototype
  5. presence is not the same as a defined value

basics

~20 s

The in operator searches the whole prototype chain, so it reports true for inherited names such as toString. Object.hasOwn checks only the object's own properties and reports false. Use hasOwn whenever you mean data the object itself carries.

solid answer

~50 s

`in` asks whether the name is reachable at all: it checks own properties and then every prototype up the chain, so `'toString' in {}` is `true` because `Object.prototype` supplies it. `Object.hasOwn(obj, 'toString')` asks only whether the object itself defines the property, so for a plain literal it is `false`. The distinction matters whenever you are inspecting data rather than capability — validating a parsed JSON payload, counting fields, or deciding whether a caller passed an option — because inherited names would otherwise produce false positives. `Object.hasOwn` was added in ES2022 as a safer replacement for `obj.hasOwnProperty(key)`, which breaks when the object has no prototype or defines its own `hasOwnProperty`. Note that both are membership checks, not value checks: a property explicitly set to `undefined` still reports `true`, whereas `obj.key !== undefined` cannot tell the two cases apart.

code

javascript · 15 lines
javascript
const obj = { a: 1, missing: undefined };

console.log('a' in obj);                       // true
console.log('toString' in obj);                // true  (inherited)
console.log(Object.hasOwn(obj, 'toString'));   // false (not own)

// presence vs value
console.log(obj.missing !== undefined);        // false
console.log(Object.hasOwn(obj, 'missing'));    // true

// why the method form is unsafe
const dict = Object.create(null);
dict.k = 1;
console.log(Object.hasOwn(dict, 'k'));         // true
// dict.hasOwnProperty('k')                    // TypeError: not a function

go deeper

for a junior

Be able to say that in finds inherited names like toString while Object.hasOwn only reports properties the object itself defines, and reach for hasOwn when inspecting plain data.

for a middle

Explain the mechanics: in runs the same chain walk as a read, hasOwn stops at the object. Cover why presence differs from a truthiness test and why hasOwn replaced calling hasOwnProperty as a method.

for a senior

Show the judgment: inherited names in data checks are a correctness and security seam for externally supplied payloads, and null-prototype objects or Map are the structural fix rather than defensive guards everywhere.

for a principal

Own the convention across a codebase — one own-property helper, data containers that cannot inherit surprises, and a lint rule — so that no individual reviewer has to spot each unsafe membership test.

## Two different questions Property access resolves by walking the prototype chain, so "does this object have `x`" is genuinely ambiguous. JavaScript gives you both readings. **`'x' in obj`** performs the internal [[HasProperty]] operation: own properties first, then each prototype in turn, up to `null`. It answers *is this name reachable*. **`Object.hasOwn(obj, 'x')`** answers *does this object itself define the name*. No walk. ```js const obj = { a: 1 }; 'a' in obj; // true 'toString' in obj; // true — from Object.prototype Object.hasOwn(obj, 'toString'); // false — not its own ``` `Reflect.has(obj, 'x')` is the function form of `in` and gives the same chain-walking answer, so it is not an alternative to `hasOwn`. ## Why hasOwn rather than hasOwnProperty `Object.prototype.hasOwnProperty` does the same own-property test, but calling it as a method has two failure modes. First, an object may not inherit it at all — `Object.create(null)` produces an object with no prototype, so `dict.hasOwnProperty('k')` throws a `TypeError`. Second, an object may define its own property with that name, which is entirely possible when the keys come from parsed JSON: ```js const payload = JSON.parse('{"hasOwnProperty": 1}'); payload.hasOwnProperty('x'); // TypeError: not a function Object.hasOwn(payload, 'x'); // false — works fine ``` The pre-ES2022 workaround was to borrow the method: `Object.prototype.hasOwnProperty.call(obj, key)`. `Object.hasOwn(obj, key)`, added in ES2022, does exactly that with far less ceremony and is the modern default. Both accept string and symbol keys. ## Membership versus value Neither check looks at the value, which is the point of using them instead of a truthiness test: ```js const opts = { retry: undefined }; opts.retry !== undefined; // false — looks absent 'retry' in opts; // true Object.hasOwn(opts, 'retry'); // true — explicitly provided ``` This matters for option objects where "not supplied" and "supplied as `undefined`/`0`/`''`" must be distinguished before applying a default. ## Choosing between them Use `Object.hasOwn` when the object is **data**: a parsed payload, a lookup table, an options bag, a record whose fields you are iterating or counting. Inherited names are noise there, and treating them as data is how a payload key like `constructor` or `toString` sneaks into logic that never expected it. Use `in` when you are asking about **capability**: does this value support a method or a protocol, including one it inherits. ```js if ('then' in value) { /* looks thenable, inherited counts */ } if (Object.hasOwn(config, 'timeout')) { /* the caller supplied it */ } ``` `in` also has a distinct meaning for arrays: it tests index presence rather than range, so it distinguishes a hole from a stored `undefined`. `1 in [1, , 3]` is `false` while `1 in [1, undefined, 3]` is `true`. ## A related trap `in` is also the operator inside a `for...in` loop, and that loop shares the chain-walking behaviour: it visits inherited enumerable string-keyed properties too. That is why code that iterates data objects is usually written with `Object.keys`, `Object.entries` or `for...of` over them, which are own-property based, rather than filtering `for...in` with a `hasOwn` guard after the fact. ## Summary rule Default to `Object.hasOwn` for data and reach for `in` deliberately when inheritance is part of the question you are asking. If you find yourself writing `in` against a payload from outside the program, that is almost always the wrong check.

  • Why do modern style guides prefer `Object.hasOwn(obj, k)` over `obj.hasOwnProperty(k)`?
    Because the method call depends on the object inheriting it. An object built with `Object.create(null)` does not have it and throws, and an object whose data happens to include a `hasOwnProperty` key shadows it with a non-function. `Object.hasOwn`, added in ES2022, performs the same own-property test without touching the object's chain.
  • How would you tell apart an option that was omitted from one explicitly passed as undefined?
    Use `Object.hasOwn(options, 'timeout')`. It reports true for a key present with value `undefined` and false when the key was never set, whereas `options.timeout !== undefined` and destructuring defaults collapse both cases into 'absent' and silently apply the default.
  • Does `Object.hasOwn` work with symbol keys?
    Yes. Own-property checks cover string and symbol keys alike, so `Object.hasOwn(obj, Symbol.iterator)` is a valid test for an own iterator. It returns false for an inherited one, which is common — array instances inherit `Symbol.iterator` from `Array.prototype` rather than owning it.

saying these in an interview costs you the question

  • Says `in` only checks the object's own properties
  • Uses obj.key !== undefined as a presence check
  • Thinks hasOwnProperty walks the prototype chain
  • Assumes every object inherits hasOwnProperty
  • Believes Reflect.has is an own-property check

context