Classes: Blueprints for Objects
A class is a blueprint for objects of one kind, with a constructor that fills in each new object and methods they all share, and new builds an object from it.
Apprentice · JavaScript · OOP · Updated
After this lesson you can
- Write a class with a constructor and methods
- Create objects from a class with new and use their properties and methods
Key takeaways
- A class is a blueprint. It describes once what every object of that kind has and can do.
- The constructor runs each time you build an object, and stores its starting values on this.
- new Ship("Comet", 100, 50) builds a new object, runs the constructor for it and gives it back. Each object it makes is an instance.
- Methods written in the class work for every instance, with this as the instance before the dot.
- Class names start with a capital letter, and a class can't be called without new.
A space game has hundreds of ships: the player's squadron, waves of enemy drones, a boss at the end of the level. Nobody types those out one by one. They write one description of a ship and build as many ships from it as the game needs. Online stores do the same for products, social apps for posts, and banking apps for accounts. That description is a class, and it's how most large JavaScript programs, and the libraries they use, are organized.
The problem: copying the same object
Here are three ships for Red Squadron, written the way you know so far:
const comet = { name: "Comet", shields: 100, fuel: 50 };
const vega = { name: "Vega", shields: 100, fuel: 50 };
const orion = { name: "Orion", sheilds: 100, fuel: 50 };
console.log(orion.shields);
undefined
Look closely at Orion: sheilds is misspelled, so Orion has no shields at all, and nothing warned anyone. Every copy is another chance for a typo, and if every ship later needs a takeHit method, you'd add it to every copy.
A blueprint and the things built from it
A builder doesn't redraw a house for every house on the street. There's one blueprint, and each house built from it gets its own rooms, its own paint and its own family.
In JavaScript the blueprint is a class. Inside it, a special method named constructor says how to fill in each new object. To build one, you write new, the class name and the starting values:
class Ship {
constructor(name, shields, fuel) {
this.name = name;
this.shields = shields;
this.fuel = fuel;
}
}
const comet = new Ship("Comet", 100, 50);
const vega = new Ship("Vega", 80, 65);
console.log(comet);
console.log(vega.fuel);
Ship { name: 'Comet', shields: 100, fuel: 50 }
65
The property names are written once, in the class, so no ship can get a misspelled one. Node even prints the class name, Ship, in front of the object. Class names start with a capital letter (Ship, not ship), which tells a reader "this is a blueprint".
What new does
new Ship("Comet", 100, 50) does three things, in order:
- It makes a new, empty object.
- It runs the constructor with
thisset to that new object, sothis.name = namestores"Comet"in it. - It gives the finished object back, and
const cometkeeps it.
Each object built from a class is called an instance of it: Comet and Vega are two instances of Ship. They're separate objects, each with its own data, so changing Vega's fuel leaves Comet's alone.
Forget the new, and JavaScript stops you:
class Ship {
constructor(name) {
this.name = name;
}
}
const comet = Ship("Comet");
TypeError: Class constructor Ship cannot be invoked without 'new'
Methods every ship shares
Methods go inside the class too, after the constructor, written the same way as on an object but with no commas between them. Every instance can use them, and this is the instance before the dot:
class Ship {
constructor(name, shields, fuel) {
this.name = name;
this.shields = shields;
this.fuel = fuel;
}
takeHit(damage) {
this.shields = Math.max(0, this.shields - damage);
}
describe() {
return `${this.name}: shields ${this.shields}, fuel ${this.fuel}`;
}
}
const comet = new Ship("Comet", 100, 50);
const vega = new Ship("Vega", 80, 65);
comet.takeHit(30);
vega.takeHit(95);
console.log(comet.describe());
console.log(vega.describe());
Comet: shields 70, fuel 50
Vega: shields 0, fuel 65
Math.max(0, ...) gives back the bigger of its two numbers, so the shields stop at 0 instead of going negative. One takeHit, written once, works on both ships, each with its own shields.

Try it. Make fleet.js with the Ship class above. Build a third ship, Orion, with 60 shields and 90 fuel, hit it for 25, and print its describe(). Then try to build a ship without new, and read the error.
Default values and a fleet of instances
A constructor's parameters can have default values, the same as any function's. Most new ships start with full shields and half a tank, so only the name is required:
class Ship {
constructor(name, shields = 100, fuel = 50) {
this.name = name;
this.shields = shields;
this.fuel = fuel;
}
describe() {
return `${this.name}: shields ${this.shields}, fuel ${this.fuel}`;
}
}
const fleet = [new Ship("Comet"), new Ship("Vega", 80), new Ship("Orion", 60, 90)];
for (const ship of fleet) {
console.log(ship.describe());
}
Comet: shields 100, fuel 50
Vega: shields 80, fuel 50
Orion: shields 60, fuel 90
Instances are ordinary objects, so they go in arrays, get passed to functions and loop like any others.
In your textbook this is called object-oriented programming (OOP for short): organizing a program around objects that bundle their data with the actions that use it, built from classes. The rest of this chapter adds its other big ideas: keeping data private, and building one class on top of another.
Check yourself
2 questions to try on the lesson page.
So what
Whenever a program has many things of one kind (ships, players, products, posts), a class describes them once, and new builds as many as you need, each with its own data and the same trusted methods.