Suggest an editImprove this articleRefine the answer for “Classes and prototypes”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**A JavaScript class is syntactic sugar over a constructor function and prototypal inheritance: it introduces no separate object model.** Declaring a class creates a function (`typeof User === 'function'`) and puts every method on `User.prototype` rather than on the instance; `new User()` creates an empty object, sets its `[[Prototype]]` to `User.prototype` and runs the constructor. `extends` just links two chains: `Child.prototype` points to `Parent.prototype`, and `Child` itself points to `Parent` for static members. ```javascript class User { sayHi() {} } const u = new User(); console.log(typeof User); // 'function' console.log(Object.getPrototypeOf(u) === User.prototype); // true console.log(Object.getOwnPropertyNames(User.prototype)); // ['constructor', 'sayHi'] ``` **Key point:** a class gives you nicer syntax, but method lookup still walks up the prototype chain, and that is exactly the check `instanceof` performs.Shown above the full answer for quick recall.Answer (EN)Image**Classes in JavaScript are a convenient wrapper over constructor functions and prototypes: the language underneath has not changed, only the syntax has.** A class creates a constructor function, puts the methods on its `prototype` and, when `extends` is present, builds a prototype chain. ## Theory ### TL;DR - A `class` is a function: `typeof User === 'function'`. - Class methods are written to `User.prototype`, not into every instance. - `new User()` creates an object, sets `User.prototype` as its prototype and runs the constructor. - `extends` links `Child.prototype` to `Parent.prototype`, and `Child` to `Parent` for static members. - Calling a method is a lookup up the prototype chain; `instanceof` checks that same chain. - The sugar is not fully transparent: class code is strict, its methods are non enumerable, and it cannot be called without `new`. ### Quick example ```javascript class User { constructor(name) { this.name = name; } sayHi() { console.log(`Hello, ${this.name}!`); } } // Roughly the equivalent before class existed: function UserOld(name) { this.name = name; } UserOld.prototype.sayHi = function () { console.log(`Hello, ${this.name}!`); }; ``` Both variants produce the same structure: data on the instance, methods on the prototype. ### What happens on `new` When you write `const user = new User('Maria')`, the engine performs four steps: 1. Creates a new empty object. 2. Sets its internal `[[Prototype]]` to `User.prototype`. 3. Runs the constructor with `this` bound to that object. 4. Returns the object, unless the constructor explicitly returns another object. ```javascript const user = new User('Maria'); console.log(Object.getPrototypeOf(user) === User.prototype); // true console.log(user.constructor === User); // true console.log(Object.keys(user)); // ['name'], data only ``` The instance stores only its own data and takes behaviour from the prototype. ### Methods are properties of `prototype` Every method declared in a class body automatically goes to `Class.prototype`, so all instances share one function in memory. ```javascript console.log(Object.getOwnPropertyNames(User.prototype)); // ['constructor', 'sayHi'] const u1 = new User('Maria'); const u2 = new User('Oleh'); console.log(u1.sayHi === u2.sayHi); // true, one method for everybody ``` This is the core idea of prototypes: do not duplicate functions in every object. The exceptions are class fields (`name = 'default'`) and methods declared as arrow function properties: those are created per instance. ### `extends` is prototypal inheritance `extends` does nothing new, it only links prototypes. ```javascript class Animal { speak() { console.log('The animal makes a sound'); } } class Dog extends Animal { bark() { console.log('Woof woof!'); } } const d = new Dog(); d.speak(); // 'The animal makes a sound' ``` Under the hood this is roughly what happens: ```javascript // links the instance prototypes: methods Object.setPrototypeOf(Dog.prototype, Animal.prototype); // links the constructors themselves: static members Object.setPrototypeOf(Dog, Animal); ``` Hence two parallel chains. For instances: ``` d -> Dog.prototype -> Animal.prototype -> Object.prototype -> null ``` For constructors: ``` Dog -> Animal -> Function.prototype -> Object.prototype -> null ``` You can confirm all of it in code: ```javascript console.log(Object.getPrototypeOf(d) === Dog.prototype); // true console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype); // true console.log(Object.getPrototypeOf(Dog) === Animal); // true ``` The second chain explains why parent static methods are reachable through the child class: `Dog.someStaticMethod()` is found on `Animal`. ### How a method is looked up and what `instanceof` checks A `d.speak()` call runs like this: 1. Does `d` itself have an own `speak` property? No. 2. The engine looks in `Dog.prototype`. No. 3. Then in `Animal.prototype`. Found, called with `this === d`. 4. If it were still missing, the search would continue to `Object.prototype` and finally yield `undefined`. `instanceof` walks the same chain: it checks whether `Constructor.prototype` appears among the object prototypes. ```javascript console.log(d instanceof Dog); // true console.log(d instanceof Animal); // true, Animal.prototype is in the chain console.log(d instanceof Object); // true ``` The `super` keyword also works through prototypes: `super.speak()` calls the method found on `Object.getPrototypeOf(Dog.prototype)` with the current `this`, while `super(...)` in a constructor invokes the parent constructor, which is what creates `this` for a derived class. ### What a class does under the hood, and how it still differs The equivalent without `class` looks like this: ```javascript function Animal() {} Animal.prototype.move = function () {}; function Dog() { Animal.call(this); } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.bark = function () {}; const dog = new Dog(); dog.bark(); ``` | Class construct | What happens under the hood | | --- | --- | | `constructor` | the body of the constructor function | | class method | a property on `Class.prototype` | | static method | a property on the `Class` function itself | | class field | an own instance property created in the constructor | | `extends` | `Child.prototype` points to `Parent.prototype`, `Child` to `Parent` | | `super()` | a call to the parent constructor, which creates `this` | | `instanceof` | a prototype chain check | The sugar is not entirely transparent though, and interviewers ask about these differences: - Class code always runs in strict mode. - Class methods are non enumerable, so they never show up in `for...in`. - A class cannot be called without `new`: doing so throws a `TypeError`. - A class declaration is not hoisted the way a `function` is: before its line it sits in the TDZ. - In a derived class `this` is unavailable until `super()` has been called. ### Common mistakes - **Treating a class as a separate OOP model.** JavaScript has no classes in the Java or C# sense: there are only objects and prototype links. - **Confusing `User.prototype` with `Object.getPrototypeOf(User)`.** The first is the prototype of future instances, the second is the prototype of the class function itself, which is where static members come from. - **Thinking methods are copied into the instance.** They live on one shared prototype, which is why `u1.sayHi === u2.sayHi`. - **Calling a class without `new`.** An old style constructor function tolerates it (and corrupts `this`), while a class throws a `TypeError`. - **Assigning arrow functions to fields without a reason.** `handle = () => {}` creates a new function per instance and never reaches the prototype, so memory grows. - **Forgetting `super()` in a derived constructor.** Without it, touching `this` throws a `ReferenceError`, because the object does not exist yet.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.