Prototypes & OOPhigh

ES6 Class Syntax

ES6 classes are syntactic sugar over prototype-based inheritance. class Foo { constructor() {} method() {} } is equivalent to a constructor function with methods on its prototype.

Memory anchor

ES6 classes are a fancy suit over the same prototype body. Under the tuxedo, it is still prototype delegation all the way down. The 'class' keyword is just JavaScript dressing up for a job interview.

Expected depth

Classes are NOT hoisted (TDZ applies). extends sets up the prototype chain for both the class and its prototype. super() in a derived constructor must be called before accessing this — it's what creates the this object when extending built-ins. Static methods are on the class (constructor function) itself, not on instances. Private fields (#field) are truly private — not on the prototype, not accessible via prototype tricks, enforced by the engine.

Deep — senior internals

Under the hood, class Foo extends Bar creates: (1) Foo.prototype = Object.create(Bar.prototype), (2) Object.setPrototypeOf(Foo, Bar) for static inheritance, (3) Foo.prototype.constructor = Foo. Private fields (#) use a WeakMap-like internal slot — they are stored per-instance and brand-checked (not name-checked) on access, which is why you can use instanceof alternatives with #field in operator: #field in obj. Class fields defined in the class body (without # or static) are syntactic sugar for this.field = ... in the constructor, adding own properties to instances — they do NOT go on the prototype.

🎤Interview-ready answer

ES6 classes are syntactic sugar over prototypes — they don't change the underlying delegation model. Methods defined in a class body go on the prototype and are shared across instances; class fields go on each instance. Private fields (#) are truly private and engine-enforced, unlike the old _underscore convention. Always call super() before accessing this in a derived class constructor.

Common trap

Class methods defined in the class body are on the prototype and are NOT bound — passing them as callbacks will lose this. The fix is either a class field arrow function (which creates a per-instance function) or explicit binding in the constructor. The tradeoff: arrow class fields sacrifice prototype sharing and break method override.

Related concepts