Prototype Chain Errors & hasOwnProperty
debt(d3/e3/b5/t7)
Closest to 'default linter catches the common case' (d3). ESLint can detect for...in loops without hasOwnProperty checks via built-in rules like no-prototype-builtins and no-restricted-syntax. The detection_hints confirm ESLint automation with the for...in pattern, making this a standard linter-level catch.
Closest to 'simple parameterised fix' (e3). The quick_fix is straightforward: replace for...in with Object.keys()/Object.entries() or use Object.hasOwn() instead of hasOwnProperty(). These are one-component refactors within isolated loops, not cross-cutting changes.
Closest to 'persistent productivity tax' (b5). This choice affects iteration patterns throughout any JavaScript codebase. Developers must remember to use Object.keys() or add hasOwnProperty checks on every for...in usage; the prototype chain's invisible reach creates an ongoing cognitive load across multiple work streams, though it doesn't fundamentally reshape the system architecture.
Closest to 'serious trap' (t7). The misconception directly contradicts the developer's intuition: for...in iterates the entire prototype chain, not just own properties—the opposite of what the syntax suggests. This contradicts similar iteration patterns in other languages (for...in in Python, for...of in modern JS) and matches a serious gotcha that catches experienced developers unaware until they encounter inherited properties or prototype pollution.
TL;DR
Explanation
Every JS object has a prototype chain. for...in iterates all enumerable properties including inherited ones. This causes bugs when libraries add properties to Object.prototype (rare but real). Safe iteration: Object.keys(obj) — own enumerable string keys, Object.entries(obj) — key-value pairs, for (const key of Object.keys(obj)). hasOwnProperty() check: obj.hasOwnProperty(key) — but safe version is Object.prototype.hasOwnProperty.call(obj, key) since obj might not have hasOwnProperty (Object.create(null)). Object.create(null) creates a prototype-free object — safe for dictionaries.
Common Misconception
Why It Matters
Common Mistakes
- Using for...in without hasOwnProperty check.
- Calling obj.hasOwnProperty() directly — fails on Object.create(null) objects.
- Not knowing Object.keys() only returns own enumerable properties.
Code Examples
const obj = { name: 'Paul', role: 'admin' };
for (const key in obj) {
// Includes prototype properties if any were added:
console.log(key, obj[key]);
}
// Safe iteration:
for (const [key, value] of Object.entries(obj)) {
console.log(key, value); // Only own properties
}
// Safe hasOwnProperty call (works on Object.create(null) too):
const has = Object.prototype.hasOwnProperty.call(obj, 'name');
// Prototype-free object for dictionaries:
const dict = Object.create(null);
dict['key'] = 'value'; // No prototype chain