Summary
key in obj returns false for every inherited property when obj was created
by Object.create(proto) — both the custom prototype's own keys and
Object.prototype members.
Repro
const o = Object.create({ mid: 1 });
o.own = 2;
console.log("own" in o); // true ✓
console.log("mid" in o); // node: true perry: false ✗
console.log("toString" in o); // node: true perry: false ✗
console.log("mid" in Object.create(Object.create({ mid: 1 }))); // node: true perry: false ✗
Root cause
Object.create(proto) models [[Prototype]] via a synthetic class_id →
prototype object (CLASS_PROTOTYPE_OBJECTS), the mechanism the field-GET path
walks (resolve_proto_chain_field). The in operator's walk
(ordinary_has_property in has_property.rs) only follows recorded static
prototypes (object_static_prototype), which Object.create never populates,
so it stops at the receiver's own keys. The Object.prototype fallback
(ordinary_object_prototype_property_value) additionally bails when
class_id != 0, and the Object.create result carries the synthetic class_id —
so even "toString" in o was reported absent.
class / Object.setPrototypeOf / __proto__ receivers were already correct.
Summary
key in objreturnsfalsefor every inherited property whenobjwas createdby
Object.create(proto)— both the custom prototype's own keys andObject.prototypemembers.Repro
Root cause
Object.create(proto)models[[Prototype]]via a synthetic class_id →prototype object (
CLASS_PROTOTYPE_OBJECTS), the mechanism the field-GET pathwalks (
resolve_proto_chain_field). Theinoperator's walk(
ordinary_has_propertyinhas_property.rs) only follows recorded staticprototypes (
object_static_prototype), whichObject.createnever populates,so it stops at the receiver's own keys. The
Object.prototypefallback(
ordinary_object_prototype_property_value) additionally bails whenclass_id != 0, and theObject.createresult carries the synthetic class_id —so even
"toString" in owas reported absent.class/Object.setPrototypeOf/__proto__receivers were already correct.