What this usually means
These symptoms stem from a misunderstanding of how JavaScript’s prototype chain resolves properties. The chain is live—mutations to prototype objects affect all descendants. Common pitfalls include assigning mutable objects (arrays, plain objects) directly to the prototype instead of initializing them per instance, forgetting to call parent constructors with the correct `this`, or accidentally shadowing properties via the prototype’s `__proto__` setter. The root cause is often that developers treat prototypes as static blueprints, but they are actually dynamic objects that can be modified at runtime.
The first ten minutes — establish facts before touching code.
- 1console.log(Object.getPrototypeOf(instance)) to inspect the immediate prototype.
- 2`instance.hasOwnProperty('prop')` to check if a property is own or inherited.
- 3`Object.getOwnPropertyNames(proto)` to list own properties of a prototype object.
- 4`for (let key in obj) console.log(key, obj.hasOwnProperty(key))` to see enumerable inherited properties.
- 5Use Chrome DevTools memory snapshot to compare objects of the same type for shared references.
- 6Add debugger statements inside the constructor to verify `this` context and property assignments.
The specific files, logs, configs, and dashboards that usually own this bug.
- searchConstructor functions: check if properties assigned to `this` vs prototype.
- searchClass `extends` clauses: verify `super()` is called and that parent constructor is invoked.
- searchObject literals used as prototypes: e.g., `Child.prototype = new Parent()` or `Child.prototype = Parent.prototype`.
- search`Object.create()` calls: inspect the prototype argument for unintended shared state.
- searchThird-party library code that extends native prototypes (Array.prototype, etc.).
- searchBrowser extensions or polyfills that modify built-in prototypes.
- searchThe actual prototype chain: `instance.__proto__.__proto__...` (use `Object.getPrototypeOf`).
Practical causes, not theory. These are the things you will actually find.
- warningAssigning a mutable object (e.g., `this.arr = []`) to the prototype instead of the instance.
- warningUsing `Child.prototype = Parent.prototype` instead of `Object.create(Parent.prototype)`.
- warningForgetting to call `Parent.call(this)` in the child constructor, so parent properties are never initialized.
- warningModifying the prototype after instances are created, causing retroactive changes.
- warningAccidental shadowing: setting a property directly on an instance that matches a prototype property name.
- warningConfusing `__proto__` (internal prototype) with `prototype` (property on constructor functions).
Concrete fix directions. Pick the one that matches your root cause.
- buildMove mutable defaults from prototype to constructor: `function Foo() { this.arr = []; }`.
- buildUse `Object.create(Parent.prototype)` to set up inheritance without invoking parent constructor.
- buildAlways call `Parent.call(this, ...args)` in child constructor to initialize parent properties.
- buildUse `Object.setPrototypeOf` only when absolutely necessary; prefer `Object.create`.
- buildFreeze prototype objects if they should never be mutated: `Object.freeze(MyClass.prototype)`.
- buildFor deep inheritance, use ES6 classes which enforce `super()` and static prototype wiring.
A fix you cannot prove is a guess. Close the loop.
- verifiedCreate multiple instances and mutate a shared array—only one instance should change after fix.
- verified`instance instanceof Parent` returns true for child instances.
- verified`Object.getPrototypeOf(childInstance) === Child.prototype`.
- verifiedUnit tests that check `hasOwnProperty` for instance-specific properties.
- verifiedSnapshot comparison in DevTools shows separate arrays/objects per instance.
- verified`Object.getOwnPropertyDescriptor(instance, 'prop')` returns `undefined` for inherited properties.
Things that make this bug worse or harder to find.
- warningUsing `__proto__` for setting prototypes; it's a deprecated getter/setter and slow.
- warningAssuming `Object.create(null)` objects have a prototype chain; they don't—they have no `toString`.
- warningModifying parent prototypes after child classes have been defined; it will affect children.
- warningUsing `new Parent()` to set up inheritance if Parent constructor has side effects (e.g., DOM manipulation).
- warningTreating `prototype` on a class as the same as `[[Prototype]]` of instances; they are different.
- warningIgnoring the order of property resolution: own properties first, then prototype chain.
Shared Cart Array Across User Sessions
Timeline
- 09:15User reports that items added to cart appear in other users' carts.
- 09:20Team reproduces locally: two browser tabs share the same cart data.
- 09:30Initial suspicion: global variable or localStorage sync.
- 09:45Checked localStorage and global state—both isolated per tab.
- 10:00Inspected Backbone Model instances; noticed `cartItems` array is same reference.
- 10:15`instance.hasOwnProperty('cartItems')` returns false for all instances.
- 10:30Found `CartModel.prototype.cartItems = []` in a base model definition.
- 10:45Fixed by moving `cartItems` initialization into constructor: `this.cartItems = []`.
- 11:00Deployed fix; verified no shared state across instances.
The report came in from our customer support: two users on different machines were seeing the same shopping cart. My first instinct was a caching layer or a shared session—but we used token-based auth and no server-side session. I opened two incognito windows and logged in as different users. Sure enough, adding an item in one tab immediately appeared in the other. This was client-side only.
I dug into our Backbone models. We had a base `CartModel` that all user carts extended. I inspected a few instances in the console: each had a `cartItems` property, but `instance.hasOwnProperty('cartItems')` returned `false`. That meant it was on the prototype. I checked `CartModel.prototype.cartItems` and saw a single array that all instances shared. The original developer had set `cartItems: []` directly on the prototype as a default, expecting it to be copied per instance—but that’s not how prototypes work. Every instance read from the same array.
The fix was simple: move `this.cartItems = []` into the constructor. But we also had to clear the existing shared array across all current user sessions—we forced a page reload and invalidated the cached model. After deployment, we verified with `hasOwnProperty` that each instance had its own array. No more cross-user cart contamination. The lesson: never put mutable objects on the prototype unless you explicitly want shared state.
Root cause
Mutable array (`cartItems`) assigned to the prototype of a Backbone model, causing all instances to share the same array reference.
The fix
Moved `this.cartItems = []` from the prototype definition into the constructor function, ensuring each instance gets its own array.
The lesson
Prototype properties are shared by reference. Always initialize mutable objects in the constructor, not on the prototype.
When you access `instance.prop`, JavaScript first checks if `instance` has an own property `prop`. If not, it walks up the prototype chain: `instance.__proto__`, then `instance.__proto__.__proto__`, and so on until it finds the property or reaches `null`. This is why assigning a default array to the prototype makes it shared—each instance that doesn't have its own array will read the same array from the prototype.
The order matters: own properties always win. That's why `hasOwnProperty` is your best friend. If `hasOwnProperty` returns false for a property that exists, it's inherited. Conversely, if you set a property directly on an instance, it shadows the prototype property. This can mask prototype bugs or create confusing overrides.
The classic pattern `Child.prototype = new Parent()` is dangerous if the `Parent` constructor has side effects or expects arguments. Also, it creates a single instance of Parent that becomes the prototype, so any mutable properties on that instance are shared. Instead, use `Child.prototype = Object.create(Parent.prototype)` and call `Parent.call(this)` in the child constructor.
ES6 classes avoid many of these pitfalls because `extends` and `super()` enforce correct prototype chain setup. However, they don't prevent you from putting mutable objects on the prototype—e.g., `class Foo { constructor() { this.arr = []; } }` is correct, but `Foo.prototype.arr = []` is still shared. The class syntax doesn't magically fix shared state; you still need to initialize in the constructor.
Sometimes a library or polyfill modifies native prototypes (e.g., `Array.prototype.includes`). If your code relies on `for...in` iteration, you might see unexpected properties. The fix is to use `hasOwnProperty` checks in loops, or better, avoid `for...in` for arrays. If you must extend native prototypes, use `Object.defineProperty` with `enumerable: false` to prevent them from appearing in enumeration.
For security, never allow user input to modify prototypes (prototype pollution attack). Sanitize object keys and freeze critical prototypes: `Object.freeze(Object.prototype)`. In Node.js, you can use `--frozen-intrinsics` flag to prevent modifications to built-in prototypes.
Write unit tests that create multiple instances and mutate mutable properties. Assert that changes affect only one instance. Use `expect(instance.hasOwnProperty('arr')).toBe(true)` to ensure properties are own. For inheritance, test `instance instanceof Parent` and `Object.getPrototypeOf(child) === Child.prototype`.
Consider using a linter rule that warns against assigning objects/arrays to prototypes (e.g., ESLint's `no-prototype-builtins` or custom rules). In code reviews, flag any assignment like `Foo.prototype.bar = []` and require justification. Automated checks can catch these bugs before they hit production.
Frequently asked questions
Why does setting a property on one instance affect all instances?
If the property is defined on the prototype (not own), all instances share the same reference. When you modify an object (e.g., push to an array), you're modifying the shared object, not assigning a new value to the instance. To avoid this, initialize mutable objects in the constructor so each instance gets its own copy.
How do I check if a property is from the prototype or the instance?
Use `instance.hasOwnProperty('prop')`. If it returns `true`, the property is own; if `false`, it's inherited from the prototype. You can also use `Object.getOwnPropertyDescriptor(instance, 'prop')`—if it returns a descriptor, it's own; if `undefined`, it's inherited.
What's the difference between `__proto__` and `prototype`?
`__proto__` is the actual prototype of an object (the internal `[[Prototype]]`). `prototype` is a property on constructor functions that becomes the `__proto__` of instances created by that constructor. Confusing them leads to inheritance bugs. Use `Object.getPrototypeOf()` to get the prototype, not `__proto__`.
Can I use `Object.create(null)` to avoid prototype chain issues?
Yes, but it creates an object with no prototype, so it won't have `toString`, `hasOwnProperty`, etc. This is useful for dictionaries (e.g., `const dict = Object.create(null)`), but for normal objects you'll lose essential methods. Only use it if you explicitly want a prototype-less object.
How do I properly set up inheritance in JavaScript?
Using ES6 classes: `class Child extends Parent { constructor(...args) { super(...args); } }`. For ES5: `Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;` and in Child constructor: `Parent.call(this, ...args);`. Avoid `new Parent()` for prototype setup.