Skip to content

correctness: __proto__ does not equal the object's prototype for plain objects and class instances, while Object.getPrototypeOf does (arrays unaffected) #10706

Description

@proggeramlug

Summary

__proto__ returns something that does not compare equal to the object's actual prototype, for plain objects and class instances. Object.getPrototypeOf is correct on the same objects, so the two disagree. Arrays are unaffected. Silent wrong answer, no error.

const o = { a: 1 };
const sink = []; sink.push(o);            // fully escaped, real heap object

o.__proto__ === Object.prototype          // perry: false   node: true
Object.getPrototypeOf(o) === Object.prototype  // perry: true    node: true   ✓

const a = [1]; sink.push(a);
a.__proto__ === Array.prototype           // perry: true    node: true   ✓
Object.getPrototypeOf(a) === Array.prototype   // perry: true    node: true   ✓

class C {} const c = new C(); sink.push(c);
c.__proto__ === C.prototype               // perry: false   node: true

Full output:

perry:  false true / true true / false
node:   true true  / true true  / true

The receivers are pushed into a live array first, so they are genuinely escaped heap objects — this is not the scalar-replacement folding of #10689, and it reproduces on a tree without that fix.

Shape of the divergence

receiver __proto__ Object.getPrototypeOf
plain object literal wrong correct
array correct correct
class instance wrong correct

That Object.getPrototypeOf is right while __proto__ is wrong on the same object means the prototype link itself is intact and the __proto__ accessor is returning a different value — a distinct object, or a re-derived intrinsic that is not the same identity as the one Object.prototype names. Arrays working suggests the array path resolves the intrinsic differently from the object and class paths.

Why it matters

__proto__ is deprecated but very widely used in existing library code for prototype identity checks — plain-object detection, clone and merge helpers, and x.__proto__ === Y.prototype tests in general. Those take the wrong branch silently rather than throwing, which is the hard failure mode to attribute from downstream.

It also means the two documented ways of asking the same question give different answers, so a reader cannot trust either without checking both.

Environment

perry built from main 68a545439 + #10611, Linux x86-64, compared against node v26.8.1. Found while fixing #10689; independent of it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions