7. Classes, methods and this
A class is a blueprint: every object it makes gets the same properties, in the same order, and shares the same behaviour. Engines love that, and so will Viscosity.
- Write a class with a constructor and methods, and create instances with new.
- Predict what this means inside a method, from how the method is called.
- Explain and fix a lost this when a method is passed as a callback.
- Explain why objects made by one class tend to share one shape.
Spot the bug
const a = { x: 0, y: 0, vx: 1, vy: 2 };
const b = { x: 5, y: 1, vx: 0, vy: 1 };
const c = { x: 2, y: 7, vX: 3, vy: 0 };
function move(p) {
p.x = p.x + p.vx;
}
move(c); // c.x becomes NaN
c was typed with vX instead of vx, so c.vx is undefined, and
2 + undefined is NaN (JavaScript’s “not a number”). Nothing warned
you. With a thousand particles written by hand, typos like this are
certain. And move floats around on its own, separate from the data it
works on.
You want a single blueprint that every particle is built from, with the code that works on particles kept right next to it.
A class is a cookie cutter. Every cookie pressed out with it has the same outline. Each cookie can still have its own icing: the same properties, with different values.
class, constructor and new
class Particle {
constructor(x, vx) {
this.x = x;
this.vx = vx;
}
}
const p = new Particle(3, 1);
console.log(p.x, p.vx); // 3 1
console.log(p);
// Particle { x: 3, vx: 1 }
new Particle(3, 1) builds a particle. Step through what it does:
class Particle {constructor(x, vx) {this.x = x;this.vx = vx;}}const p = new Particle(3, 1);const q = new Particle(8, -2);
A class is a blueprint for objects of the same kind[1].
Its constructor is the function that sets up each new object.
new creates a fresh empty object, runs the constructor with this
meaning that object, and returns it. The result is an instance of the
class. Every new makes a separate instance, with its own properties.
Methods and this
Functions written inside the class body, next to the constructor, are
methods. Here is move, now living with the data:
class Particle {
constructor(x, vx) {
this.x = x;
this.vx = vx;
}
move() {
this.x = this.x + this.vx;
}
}
const p = new Particle(0, 2);
const q = new Particle(10, -1);
p.move();
q.move();
console.log(p.x, q.x); // 2 9
There’s one move, and two particles. How does move know which one to
change? It doesn’t take a parameter. It uses this.
this works like the word “me”. The sentence “move me forward” is the
same sentence whoever says it, but it moves whoever is speaking. In
p.move(), p is speaking.
class Particle {// constructor as beforemove() {this.x = this.x + this.vx;}}const p = new Particle(0, 2);const q = new Particle(10, -1);p.move();q.move();
Compare that with last chapter. What a function can see is decided by
where it is written. What this means is decided by how it is
called.
class Particle {
constructor(x, vx) {
this.x = x;
this.vx = vx;
}
move() {
this.x = this.x + this.vx;
}
}
const ps = [
new Particle(0, 2),
new Particle(10, -1),
];
for (let i = 0; i < ps.length; i++) {
ps[i].move();
}
console.log(ps);
console.log(ps[0] instanceof Particle);
console.log(ps[0].move === ps[1].move);Losing this
Since this comes from the dot, a method called without a dot has no
object to use:
const m = p.move;
m();
// a TypeError: inside move,
// this is undefined
m is the same function, but m() has nothing before a dot, so inside it
this is undefined. (Class code always runs in JavaScript’s strict
mode, which is what makes it undefined rather than something
stranger[1].) The exact error message differs from
browser to browser.
This bites most often with callbacks. Passing p.move passes only the
function, not p:
function twice(f) {
f();
f();
}
twice(p.move); // TypeError
twice(() => p.move()); // works
The arrow function keeps the dot: when twice calls it, it calls
p.move() properly, with p before the dot.
p.move (no parentheses) is just the function. It doesn’t remember p.
To hand a method to someone else, wrap it: () => p.move().
Which class made it? instanceof
console.log(p instanceof Particle); // true
const fake = { x: 0, vx: 2 };
console.log(fake instanceof Particle);
// false
fake has exactly the same properties, but it wasn’t made by
new Particle, and instanceof can tell.
a instanceof C is true when the object a was made by the class
C (precisely: when the object holding C’s shared methods is in the
chain of objects that a borrows methods from[4]). It checks where an object came from, not
what it contains.
Every new Particle(...) adds x and then vx, in the same order, through
the same constructor. Remember shapes from chapter 3: engines give objects
that have the same properties, added in the same order, one shared hidden
shape, and then they know where x sits in every one of them without
searching[5]. A class is the simplest way to make
thousands of objects that share a shape.
That is also good news for Viscosity. An array whose elements all came from
new Particle(...) is the ideal candidate for a struct of arrays: every
record has the same fields. It’s a strong hint, not a promise, though.
Nothing stops code from later writing p.color = 'red' on one particle, so
in chapter 97 Viscosity checks whether a shape can change after the
objects are made. And in chapter 43, you’ll implement classes, instances
and this yourself, inside your own interpreter.
Your turn
Write a class Vec. new Vec(x, y) stores x and y. The method add(o) returns a new Vec that is the sum of this vector and o, without changing either. The method len() returns the length, Math.sqrt(x * x + y * y).
class Vec {
constructor(x, y) {
// your code here
}
}
const v = new Vec(3, 4);
console.log(v);Write a class Stack. push(v) puts a value on top. pop() removes the top value and returns it (or undefined if the stack is empty). peek() returns the top value without removing it. size() returns how many values it holds. Arrays have a pop() method that removes and returns the last slot, which will help. Part IV’s virtual machine is built around exactly this.
class Stack {
// your code here
}
const s = new Stack();
s.push(1);
s.push(2);
console.log(s.pop());What you learned
- A class is a blueprint. Its constructor sets up each object.
newmakes an empty object, runs the constructor withthisset to it, and returns it: an instance.- Methods live in the class body and are shared by all instances.
- Inside a method,
thisis the object before the dot in the call. A method called without a dot loses itsthis, so wrap it:() => p.move(). instanceofchecks which class made an object.- Objects from one constructor tend to share one shape, which helps engines and Viscosity alike.
Next: Map and Set, two built-in classes for looking things up fast.
Check your understanding
0/6- 1.
What is the first thing
new Particle(3, 1)does? - 2.
What does this print?
const p = new Particle(0, 1); const q = new Particle(0, 5); q.move(); console.log(p.x, q.x); - 3.
const m = p.move; m();What happens? - 4.
const o = { x: 0, vx: 2 };What iso instanceof Particle? - 5.
Where does the
movemethod of 1000 Particle instances live? - 6.
Why do classes help engines like V8 run property access fast?
References
- [1] MDN contributors. Classes. MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes (checked 2026-10-31)
- [2] MDN contributors. Using classes (JavaScript Guide). MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes (checked 2026-10-31)
- [3] MDN contributors. this. MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this (checked 2026-10-31)
- [4] MDN contributors. instanceof. MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof (checked 2026-10-31)
- [5] Mathias Bynens, Benedikt Meurer (2018). JavaScript engine fundamentals: Shapes and Inline Caches. mathiasbynens.be. mathiasbynens.be/notes/shapes-ics (checked 2026-10-31)