Suggest an editImprove this articleRefine the answer for “Class vs constructor function”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**A class is not just another notation for a constructor function: it behaves differently at call time.** A class cannot be called without `new` (you get a `TypeError`), its whole body always runs in strict mode, its methods are non-enumerable, and the class itself is not hoisted (touching it before the declaration throws a `ReferenceError`). A constructor function does the opposite in every one of these points: it can be called without `new` and silently corrupt data, it obeys the ambient mode, its prototype methods show up in `for...in`, and it can be called before its declaration. Inheritance in classes is `extends` plus `super`, while for functions it is a manual chain of `Object.create` and `call`. Private `#` fields are available only to classes. **Key point:** classes are safer and more predictable, so in new code a constructor function is used only when you must support an old environment.Shown above the full answer for quick recall.Answer (EN)Image**A class is syntactic sugar over prototypes, but not only sugar: it adds several hard behavioural rules a constructor function does not have.** Both mechanisms eventually create an object with a prototype, yet a class forbids calls without `new`, turns on strict mode, hides methods from enumeration and is not hoisted up the scope. ## Theory ### TL;DR - A class cannot be called without `new`: that is a `TypeError`. A constructor function can be, and it silently pollutes the global object or throws. - A class body always runs in strict mode, even without `'use strict'`. - Class methods are non-enumerable, so they never show up in `for...in` or `Object.keys(Prototype)`. - Classes are not hoisted: touching a class before its declaration throws a `ReferenceError`. - Class inheritance is `extends` plus `super`, for functions it is manual `Object.create` and `call`. - Private `#field` members exist only in classes. ### Quick example ```javascript function UserFn(name) { this.name = name; } UserFn("Alice"); // works, but writes to the global object or throws in strict mode class UserClass { constructor(name) { this.name = name; } } UserClass("Alice"); // TypeError: Class constructor UserClass cannot be invoked without 'new' ``` ### Syntax and readability **Constructor function:** ```javascript function User(name) { this.name = name; } User.prototype.greet = function () { console.log(`Hi, ${this.name}`); }; const user = new User("Alice"); user.greet(); ``` **Class:** ```javascript class User { constructor(name) { this.name = name; } greet() { console.log(`Hi, ${this.name}`); } } const user = new User("Alice"); user.greet(); ``` The class looks cleaner and more declarative, especially once inheritance appears. A constructor function forces you to wire up `prototype`, `Object.create()` and similar things by hand. ### Calling without new, and strict mode A constructor function can be called without `new` by accident, and that leads to errors: `this` becomes `undefined` in strict mode or binds to the global object. ```javascript function User(name) { this.name = name; } const u = User("Alice"); // error, or it creates a global window.name property ``` A class cannot be called without `new`, that is an immediate exception: ```javascript class User { constructor(name) { this.name = name; } } const u = User("Alice"); // TypeError: Class constructor User cannot be invoked without 'new' ``` This makes classes safer. On top of that, the whole class body automatically runs in strict mode even if `'use strict'` is written nowhere. A constructor function gives no such guarantee: without an explicit `'use strict'` it runs in sloppy mode. ### Enumerability, class fields and hoisting Methods defined inside `class {}` are non-enumerable, so they do not appear in a `for...in` walk, whereas methods on the `prototype` of a constructor function are enumerable: ```javascript class A { m() {} } console.log(Object.keys(A.prototype)); // [] function B() {} B.prototype.m = function() {}; console.log(Object.keys(B.prototype)); // ["m"] ``` That makes classes "cleaner" for iteration and serialisation. Inside a class body you could not declare methods as ordinary function expressions: ```javascript class A { m = function() {}; // SyntaxError in older engine versions } ``` You need the class fields syntax or arrow functions for that, and those were standardised later (ES2022 and newer). Finally, classes are not hoisted. Constructor functions can be called before they are declared: ```javascript const a = new User(); function User() {} ``` Classes do not work that way: ```javascript const a = new User(); // ReferenceError class User {} ``` This prevents surprising runtime errors. ### Inheritance With constructor functions you have to write a lot of boilerplate: ```javascript function Animal(name) { this.name = name; } Animal.prototype.speak = function() { console.log(`${this.name} makes a sound`); }; function Dog(name) { Animal.call(this, name); } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; ``` And with classes: ```javascript class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound`); } } class Dog extends Animal { speak() { console.log(`${this.name} barks`); } } ``` Short and clear. A class also gives you `super` for reaching the parent implementation, while with functions you have to call `Animal.prototype.speak.call(this)` manually. ### Summary table | Difference | Class | Constructor function | | --- | --- | --- | | Call without `new` | `TypeError` | Works, but leads to bugs | | Strict mode | Always on | Off by default | | Hoisting | No | Yes | | Methods enumerable | No | Yes | | Inheritance syntax | Simple (`extends`, `super`) | Complex (`Object.create`, `call`) | | Private fields | Yes (`#field`) | No | | Readability | High | Low | ### Common mistakes - Saying in an interview that "a class is just sugar" and stopping there. The behavioural differences (calls without `new`, strict mode, hoisting, enumerability) are exactly what is being tested. - Confusing the lack of hoisting with the declaration not being hoisted at all: the class binding is hoisted but sits in the temporal dead zone, which is why you get a `ReferenceError` and not `is not defined`. - Forgetting `Dog.prototype.constructor = Dog` in manual inheritance and then wondering why `instance.constructor` points at `Animal`. - Treating `_field` in a constructor function as private. Real privacy exists only in class fields with `#`. - Passing a class method as a callback and losing `this`: neither a class nor a constructor function binds the context for you.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.