easyJS ES5#58

hasOwnProperty vs in

Prompt

Write isOwnProp(obj, prop) that checks whether a property is an object's OWN property (vs inherited). It must survive two hostile inputs: an object that shadows hasOwnProperty with its own lying version, and an object created with Object.create(null).

Solution

function isOwnProp(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop)
}
Mentor's take

The own-vs-inherited distinction exists because property lookup walks the prototype chain: 'toString' in {} is true even though the empty object owns nothing — in answers "reachable anywhere on the chain," hasOwnProperty answers "physically on this object." Serialization, iteration guards, and dictionary-style objects all need the second question.

The one-liner is deliberately paranoid, and the paranoia is the interview content. obj.hasOwnProperty(prop) — the naive call — resolves hasOwnProperty through the prototype chain like any other property, which means it breaks in two real cases. First, an object can shadow it: { hasOwnProperty: function() { return true } } makes the naive call return whatever the attacker (or careless teammate, or JSON payload) decided. Second, Object.create(null) objects — the standard choice for lookup maps precisely because they inherit nothing — have no hasOwnProperty at all; the naive call throws. Borrowing the canonical method from Object.prototype and invoking it with .call(obj, prop) sidesteps both, because you never resolve the method through the untrusted object.

Modern escape hatch worth naming: Object.hasOwn(obj, prop) (ES2022) is this exact pattern as a built-in static. In ES5 code, .call is the idiom.

Red flag: obj.hasOwnProperty(prop) straight off the object. ESLint's no-prototype-builtins rule exists because this bites in production — data-shaped objects come from the network, and the network doesn't promise a clean prototype.

Say it: "I call Object.prototype.hasOwnProperty.call(obj, prop) because resolving the method through the object itself trusts a chain that can be shadowed or absent — same reason Object.hasOwn was added."