1import{t as e}from"./chunk-CvV0FUaE.js";var o={id:`classes`,moduleId:`objects-protos`,trackId:`core-js`,title:`Classes, #private & static blocks`,blurb:`Sugar with sharp edges: field order, private brand checks, static init.`,minutes:12,level:`working`,tldr:{model:"`class` is mostly sugar over prototypes, but it adds things functions cannot express: a TDZ, strict mode, new-only calls, non-enumerable methods and real private state.",gotcha:"Construction order is `super()`, then subclass fields, then the body â so a parent constructor calling an overridden method reads the child's fields as `undefined`."},blocks:[{kind:`prose`,md:'`class` is mostly syntax over the prototype machinery you already know: methods land on `Prototype.prototype`, `extends` links the two prototype chains, `super` is a reference to the parent\'s.\n\nBut "mostly" is doing work. Classes added things that are **not** expressible with functions â a temporal dead zone, non-enumerable methods, a hard requirement to call `new`, genuinely private state, and a fixed initialisation order that catches people out in constructors.'},{kind:`code`,lang:`js`,caption:`What class actually produces`,code:`class Point { 2 constructor(x) { this.x = x; } 3 show() { return this.x; } 4} 5 6// Methods are on the prototype, and non-enumerable 7console.log(Object.hasOwn(Point.prototype, 'show')); // true 8console.log(Object.keys(Point.prototype)); // [] 9console.log(typeof Point); // 'function' 10 11// Unlike a function, it cannot be called without new 12try { Point(1); } catch (e) { console.log(e.constructor.name); } // TypeError`},{kind:`callout`,tone:`tip`,title:`Class bodies are always strict mode`,md:"Everything inside a class body runs in strict mode regardless of the surrounding file, and class declarations are **hoisted but in the TDZ** â referencing one before its definition throws `ReferenceError`, not `undefined`."},{kind:`heading`,text:`Fields vs prototype methods`},{kind:`prose`,md:"A class field is assigned to **the instance**, inside the constructor, once per object. A method is defined **once on the prototype** and shared.\n\nThat makes `m = () => {}` and `m() {}` genuinely different declarations, not styles. The arrow field captures `this` per instance and survives being detached â at the cost of one closure per object, and of being invisible on the prototype (so it cannot be spied on or overridden the usual way)."},{kind:`code`,lang:`js`,caption:`Where each one lives`,code:`class E { 13 m = () => 'field'; 14 p() { return 'proto'; } 15} 16const e = new E(); 17 18console.log(Object.hasOwn(e, 'm')); // true \u2014 per instance 19console.log(Object.hasOwn(e, 'p')); // false 20console.log(Object.hasOwn(E.prototype, 'p')); // true \u2014 shared`,output:`true 21false 22true`},{kind:`compare`,aLabel:`Arrow field`,bLabel:`Prototype method`,rows:[{label:`Lives on`,a:`Each instance`,b:`The prototype`},{label:`this when detached`,a:`Still the instance`,b:`undefined`},{label:`Memory`,a:`One closure per instance`,b:`One function total`},{label:`Overridable by a subclass`,a:`Shadowed, not overridden`,b:`Yes`},{label:`super`,a:`Not available`,b:`Available`}]},{kind:`heading`,text:`Initialisation order`},{kind:`prose`,md:`The order inside a constructor is fixed and it explains a classic bug: 23 241. \`super(...)\` runs the parent constructor **to completion**. 252. Then the subclass's own fields are initialised, in source order. 263. Then the rest of the subclass constructor body runs. 27 28So if the parent constructor calls a method the child overrides, that method runs **before the child's fields exist**.`},{kind:`code`,lang:`js`,caption:`The parent calls an overridden method too early`,code:`class A { 29 constructor() { this.init(); } 30 init() { console.log('A.init'); } 31} 32class B extends A { 33 x = 1; 34 init() { console.log('B.init, x =', this.x); } 35} 36 37new B();`,output:`B.init, x = undefined`},{kind:`callout`,tone:`gotcha`,title:`Do not call overridable methods from a constructor`,md:"`this.init()` dispatches to the subclass, which then reads fields that `super()` has not let it initialise yet. Either do the work inline in the constructor, or expose an explicit `start()` the caller invokes after construction."},{kind:`heading`,text:`Private fields`},{kind:`prose`,md:"`#name` is real privacy, not a convention. It is not a property: it does not appear in `Object.keys`, `JSON.stringify`, `Reflect.ownKeys` or a `Proxy` trap, and it cannot be reached with `obj['#name']`.\n\nAccess is **lexically scoped to the class body** and checked by a *brand*: the object must have been constructed by that exact class. Touching `#name` on anything else throws a `TypeError` rather than returning `undefined`, which makes private fields a reliable instance test."},{kind:`code`,lang:`js`,caption:`The brand check, and the ergonomic form of it`,code:`class C { 38 #v = 1; 39 get v() { return this.#v; } 40 static has(o) { return #v in o; } // ergonomic brand check, never throws 41} 42 43console.log(C.has(new C()), C.has({})); 44 45const getter = Object.getOwnPropertyDescriptor(C.prototype, 'v').get; 46try { getter.call({}); } catch (e) { console.log(e.constructor.name + ':', e.message); }`,output:`true false 47TypeError: Cannot read private member #v from an object whose class did not declare it`},{kind:`callout`,tone:`new`,title:`Use #field in obj instead of try/catch`,md:'The `in` form is the supported way to ask "is this one of mine?" â it is cheaper and clearer than wrapping an access in `try/catch`, and unlike `instanceof` it cannot be fooled by `Object.create(C.prototype)` or a changed `Symbol.hasInstance`.'},{kind:`heading`,text:`Static members and static blocks`},{kind:`prose`,md:"`static` puts a member on the **constructor** rather than the prototype, and statics are inherited: `class D extends C {}` gives `D` access to `C`'s statics, because `Object.getPrototypeOf(D) === C`.\n\nA `static { ... }` block runs once, at class-definition time, with `this` bound to the class and full access to its private names. It exists because some setup needs statements â a loop, a try, or wiring two classes together â which a single field initialiser expression cannot express."},{kind:`code`,lang:`js`,caption:`Static initialisation that needs statements`,code:`class D { 48 static reg = new Map(); 49 static { 50 D.reg.set('k', 'v'); 51 } 52} 53console.log(D.reg);`,output:`Map(1) { 'k' => 'v' }`},{kind:`callout`,tone:`interview`,title:`How this gets asked`,md:'Favourites: *"is class just syntactic sugar?"* (mostly, but list what is not: TDZ, strict mode, new-only, non-enumerable methods, real private state), *"arrow field vs method"*, and a constructor-order trace like the `B.init` one above.'},{kind:`takeaways`,items:[`Methods go on the prototype and are non-enumerable; fields are per-instance.`,`Classes are TDZ-hoisted, always strict, and throw if called without new.`,`Order is super(), then subclass fields, then the constructor body â so a parent calling an overridden method sees undefined fields.`,`An arrow field binds this per instance but costs a closure each and cannot use super.`,`#private is brand-checked: reading it off a foreign object throws, and #x in obj is the safe test.`,`Statics live on the constructor and are inherited; static blocks run once, at definition time.`]}],cards:e};export{o as CLASSES};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.