Difference between in operator and hasOwnProperty() method in JavaScript
The in operator checks if a property exists anywhere in an object's prototype chain, including inherited ones. hasOwnProperty() looks only at the object's own properties and skips the chain entirely.
Theory
TL;DR
inis like asking "does this house or any ancestor have a garage?"hasOwnProperty()asks "does this specific house have one?"- Main difference:
inwalks up the prototype chain,hasOwnProperty()stops at the object itself - Both work with non-enumerable own properties.
Object.keys()is the one that filters by enumerability, nothasOwnProperty() - Use
infor full existence checks or pre-access safety. UsehasOwnProperty()to exclude inherited properties
Quick example
const parent = { skill: 'coding' };
const child = Object.create(parent);
child.name = 'Alice';
console.log('name' in child); // true (own property)
console.log('skill' in child); // true (inherited from parent)
console.log(child.hasOwnProperty('name')); // true
console.log(child.hasOwnProperty('skill')); // false (lives on parent, not child)in found both properties. hasOwnProperty() returned false for skill because it belongs to parent, not to child directly.
Key difference
in triggers the [[HasProperty]] internal method which walks the [[Prototype]] chain upward until it finds a match or hits null. hasOwnProperty() goes straight to the object's local property map and never looks up the chain. Both methods see non-enumerable own properties. The one that filters by enumerability is Object.keys(), not hasOwnProperty().
When to use
- Checking before accessing a prop (safety guard before
obj.prop): usein - Full feature detection, including browser APIs on prototypes: use
in - Filtering direct keys inside a
for...inloop: usehasOwnProperty() - Serializing only user-defined properties, not defaults inherited from a prototype: use
hasOwnProperty() - Protecting against prototype pollution in library code: use
hasOwnProperty()
Comparison table
| Characteristic | in | hasOwnProperty() |
|---|---|---|
| Searches prototype chain | Yes | No (own only) |
| Non-enumerable own properties | Returns true | Returns true |
Null-prototype objects (Object.create(null)) | Works fine | Throws TypeError if called directly |
| Performance | Slower (chain traversal) | Faster (local lookup) |
| Typical use case | Safe access, feature detection | for...in filtering, serialization |
How JavaScript handles this
in calls the ECMAScript [[HasProperty]] internal method, which follows [[Prototype]] links until it finds the key or reaches null. hasOwnProperty() calls OrdinaryOwnPropertyKeys directly on the object's property map. That is why hasOwnProperty() runs roughly 2-3x faster in tight loops.
Common mistakes
Mistake: for...in without filtering own properties
function User() {}
User.prototype.greet = function() {};
const user = new User();
user.name = 'Alice';
for (let key in user) {
console.log(key); // 'name', then 'greet' (inherited!)
}for...in iterates all enumerable properties, including inherited ones. Fix it with hasOwnProperty():
for (let key in user) {
if (user.hasOwnProperty(key)) {
console.log(key); // only 'name'
}
}Or just use Object.keys(user), which returns own enumerable keys directly.
Mistake: calling hasOwnProperty on a null-prototype object
const config = Object.create(null); // no prototype
config.port = 3000;
config.hasOwnProperty('port'); // TypeError: config.hasOwnProperty is not a functionObjects created with Object.create(null) have no prototype, so they do not inherit hasOwnProperty. Fix:
Object.prototype.hasOwnProperty.call(config, 'port'); // true
// Or the cleaner modern option:
Object.hasOwn(config, 'port'); // true (ES2022)I ran into this in a Node.js config loader where null-prototype objects were used to prevent prototype pollution. Object.hasOwn fixed it cleanly.
Mistake: thinking hasOwnProperty skips non-enumerable properties
const obj = {};
Object.defineProperty(obj, 'hidden', { value: 99, enumerable: false });
console.log('hidden' in obj); // true
console.log(obj.hasOwnProperty('hidden')); // true (not false!)
console.log(Object.keys(obj)); // [] <- this one skips non-enumerablehasOwnProperty() does not care about enumerability. It returns true for any own property. Object.keys() is the one that requires enumerable: true.
Real-world usage
- React:
for...in+hasOwnPropertyinReactElementValidatorto check propTypes (React 18) - Express:
req.hasOwnProperty('user')to guard against prototype leaks in middleware - Lodash:
_.has(object, path)usesin-style logic for safe deep property access - Node.js: null-prototype objects in config parsers, paired with
Object.hasOwn
Follow-up questions
Q: What happens when you use in on an Object.create(null) object?
A: It works for own properties but finds nothing inherited, since there is no prototype chain. 'toString' in Object.create(null) returns false.
Q: What is Object.hasOwn() and how does it differ from hasOwnProperty()?
A: Object.hasOwn(obj, key) was added in ES2022. It does the same thing but works correctly on null-prototype objects. Prefer it in new code.
Q: How to safely iterate only own keys?
A: Use Object.keys(obj) for own enumerable keys, or for...in with a hasOwnProperty() filter. Object.keys is the cleaner option.
Q: In a Proxy-trapped object, does in respect custom behavior?
A: Yes. in calls the Proxy's has trap, so you can intercept the check. hasOwnProperty() bypasses the trap and goes directly to the target object. This is defined in the ES2022 spec and matters in metaprogramming contexts.
Examples
Checking inherited vs own in a class hierarchy
function Animal(name) {
this.name = name;
}
Animal.prototype.type = 'animal';
const dog = new Animal('Rex');
console.log('name' in dog); // true (own property)
console.log('type' in dog); // true (inherited from Animal.prototype)
console.log(dog.hasOwnProperty('name')); // true
console.log(dog.hasOwnProperty('type')); // falsetype lives on Animal.prototype, not on dog. in finds it anyway. hasOwnProperty() does not. This is the clearest way to see the difference.
Serializing only user-defined settings
const defaults = { theme: 'light', lang: 'en' };
const userConfig = Object.create(defaults);
userConfig.lang = 'uk';
userConfig.fontSize = 16;
// Save only what the user explicitly set
const toSave = {};
for (let key in userConfig) {
if (userConfig.hasOwnProperty(key)) {
toSave[key] = userConfig[key];
}
}
console.log(toSave); // { lang: 'uk', fontSize: 16 }
// 'theme' stays out because it is on defaults, not on userConfigA pattern that comes up in configuration systems. Without the hasOwnProperty check, default values the user never touched would end up in the saved output.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.