JavaScript Object Constructors
An object constructor is a regular JavaScript function that is designed to be called with the new keyword in order to build many similar objects without repeating the same property-assignment code over and over. Instead of writing out an object literal by hand every time you need a “person” or a “car”, you write the blueprint once as a function, and then stamp out as many instances as you like. Understanding constructors is essential because they are the historical foundation that the modern class syntax is built on top of — under the hood, a class is still just a constructor function with a prototype.
Overview / How it works
In JavaScript, functions are not just callable pieces of code — they are also objects, and every ordinary function has a special property called prototype. This property holds an object that will become the prototype of every instance created from that function when it is invoked with new. A constructor function is simply a function meant to be used this way: by convention it is named with a capital letter (Person, not person) so that anyone reading the code immediately knows “this must be called with new“.
When you write this.name = name; inside a constructor, this refers to the brand-new object that new is in the process of creating. Any properties you attach to this become that instance’s own, enumerable, per-object properties. Methods, on the other hand, are usually placed on ConstructorName.prototype rather than directly on this, because the prototype is shared by every instance — this means one single function in memory is reused by all objects, instead of every object carrying its own private copy of every method (which would waste memory if you created thousands of instances).
JavaScript also ships with a built-in generic Object() constructor. Calling new Object() creates a plain empty object, equivalent to {}. You will rarely use new Object() directly in real code — object literals are shorter and just as capable — but every object literal you write is, in a sense, an instance of the built-in Object constructor, which is why every plain object inherits methods like toString() and hasOwnProperty() from Object.prototype.
Syntax
function ConstructorName(param1, param2) {
this.prop1 = param1;
this.prop2 = param2;
}
ConstructorName.prototype.methodName = function () {
// shared behavior using this.prop1, this.prop2
};
const instance = new ConstructorName(value1, value2);
| Part | Meaning |
|---|---|
function ConstructorName(...) |
A normal function declaration; capitalized name signals it is meant to be used with new. |
this.prop = param |
Attaches an own property to the object being constructed. |
ConstructorName.prototype.methodName |
Adds a method that every instance will share via the prototype chain. |
new ConstructorName(...) |
Creates a new object, links its prototype, runs the function with this bound to that object, and returns it. |
Examples
Example 1: A basic constructor function
function Person(name, age) {
this.name = name;
this.age = age;
}
const alice = new Person("Alice", 30);
console.log(alice.name, alice.age);
Output:
Alice 30
Calling new Person("Alice", 30) creates a fresh object, runs the Person function with this pointing at that object, and the two assignment lines give it its own name and age properties. The result is stored in alice, a fully independent object.
Example 2: Sharing methods through the prototype
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function () {
return `Hi, I'm ${this.name} and I'm ${this.age} years old.`;
};
const bob = new Person("Bob", 25);
console.log(bob.greet());
Output:
Hi, I'm Bob and I'm 25 years old.
Here greet is defined once on Person.prototype, not recreated inside the constructor. Every Person instance can call .greet(), and inside that method this automatically refers to whichever instance made the call.
Example 3: Multiple instances and instanceof
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.describe = function () {
return `${this.make} ${this.model}`;
};
const car1 = new Car("Toyota", "Corolla");
const car2 = new Car("Honda", "Civic");
console.log(car1.describe());
console.log(car2.describe());
console.log(car1 instanceof Car);
console.log(car1.describe === car2.describe);
Output:
Toyota Corolla
Honda Civic
true
true
Each call to new Car(...) produces a distinct object with its own make and model, but both instances point to the exact same describe function object on the shared prototype, which is why the final comparison prints true. The instanceof operator checks whether Car.prototype appears anywhere in car1‘s prototype chain, confirming it was built by this constructor.
Under the hood: what new actually does
When the JavaScript engine evaluates new ConstructorName(args), it performs four steps, in this exact order:
- A brand-new, empty plain object is created in memory.
- That new object’s internal prototype link is set to point at
ConstructorName.prototype, which is why instances can find methods likegreetordescribeeven though those methods were never copied onto the instance itself. - The constructor function body runs with
thisbound to the new object, and the arguments you passed are supplied as parameters. - If the constructor does not explicitly
returnan object, the engine automatically returns the new object thatthispointed to. If the constructor does return an object explicitly, that returned object is used instead, and the newly created one is discarded.
That last rule is subtle and trips people up, so it is covered directly in the mistakes below.
Common Mistakes
Mistake 1: Forgetting the new keyword
If you call a constructor like a normal function, without new, this is never bound to a fresh object. In strict mode (which all ES modules and classes use automatically), this is undefined inside a plain function call, so trying to set a property on it throws immediately:
"use strict";
function Person(name) {
this.name = name;
}
const p = Person("Eve");
console.log(p);
Output:
TypeError: Cannot set properties of undefined (setting 'name')
The fix is simply to always call the function with new:
"use strict";
function Person(name) {
this.name = name;
}
const p = new Person("Eve");
console.log(p);
Output:
Person { name: 'Eve' }
In non-strict code the same mistake does not throw an error — instead this silently falls back to the global object, and you end up leaking properties onto it, which is arguably worse because the bug goes unnoticed. This is one of the main reasons modern JavaScript favors class syntax: calling a class constructor without new always throws a clear TypeError, no matter what mode you are in.
Mistake 2: Not understanding how a return value inside a constructor is handled
If a constructor explicitly returns an object, that object silently replaces the one new was building, and anything you attached to this is lost:
function Widget(id) {
this.id = id;
return { overridden: true };
}
const w = new Widget(42);
console.log(w);
Output:
{ overridden: true }
But if the constructor returns a primitive value (a string, number, boolean, and so on), the return value is ignored completely and the normally-constructed object is returned instead:
function Widget(id) {
this.id = id;
return 42;
}
const w = new Widget(7);
console.log(w);
Output:
Widget { id: 7 }
The safest habit is to never return anything from a constructor at all, and let new do its job automatically.
Best Practices
- Capitalize constructor function names (
Person,Car) so every caller knows to usenew. - Put shared behavior on
ConstructorName.prototype, not inside the constructor body, so it isn’t recreated for every instance. - Prefer modern
classsyntax for new code — it compiles down to the same constructor-plus-prototype mechanics, but enforcesnewand reads more clearly. - Avoid returning a value from a constructor unless you deliberately want to override the created instance — it is a rare, advanced pattern (like implementing a singleton or a factory-like wrapper), not a default habit.
- Use
instanceofto check what a value was constructed from, rather than inspecting property shapes manually. - Never rely on non-strict mode’s silent global-object fallback when
newis omitted — treat that behavior as a bug to fix, not a feature to use.
Practice Exercises
- Write a
Bookconstructor that takestitleandauthorparameters and stores them as own properties. Add asummary()method on its prototype that returns a string like"Dune by Frank Herbert". - Create two instances of your
Bookconstructor and logbook1.summary === book2.summary. Explain in your own words why the result istrueeven thoughbook1andbook2are different objects. - Write a constructor called
Counterthat stores acountproperty starting at 0, with prototype methodsincrement()andgetCount(). Then intentionally callCounter()withoutnewinside a"use strict"script and predict what error you will get before checking.
Summary
- A constructor function is an ordinary function, conventionally capitalized, intended to be invoked with
new. - Calling
new ConstructorName(...)creates a new object, links its prototype toConstructorName.prototype, runs the function withthisbound to that object, and returns it automatically. - Shared methods belong on the prototype, not inside the constructor, so every instance reuses the same function in memory instead of duplicating it.
- Forgetting
newis a classic bug: in strict mode it throws aTypeError; in non-strict mode it silently pollutes the global object. - If a constructor explicitly returns an object, that object replaces the newly built instance; returning a primitive is ignored.
- Modern
classsyntax builds on exactly this constructor-and-prototype model, while adding safety like enforcingnew.
