How to add a method to a class
You add a method to a class by simply writing it in the class body: greet() { ... }, after which it lands on the prototype and becomes available to every instance. Besides the ordinary method, JavaScript gives you static methods, getters and setters, private methods with #, and method fields holding an arrow function, and each variant solves its own problem.
Theory
TL;DR
- An ordinary method
m() {}in the class body lands onClass.prototypeand is shared by all instances. - A method can also be attached after the declaration:
Class.prototype.m = function () {}. static m() {}belongs to the class itself, instances do not see it.getandsetprovide access that looks like a plain property.#m() {}is a genuinely private method, reachable only from inside the class.- An arrow field
m = () => {}never losesthis, which makes it handy for callbacks.
Quick example
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hi, ${this.name}!`);
}
}
const u = new User("Alice");
u.greet(); // Hi, Alice!Ordinary method, and adding a method later
An ordinary (public) method is available on every instance of the class:
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hi, ${this.name}!`);
}
}
const u = new User("Alice");
u.greet(); // Hi, Alice!Such methods are automatically added to User.prototype, which means they are shared by all instances instead of being recreated for every object.
A method can also be put on the class prototype after the declaration, by hand:
class User {
constructor(name) {
this.name = name;
}
}
// Adding the method later
User.prototype.sayBye = function () {
console.log(`${this.name} says: bye!`);
};
const u = new User("Alice");
u.sayBye(); // Alice says: bye!This works because under the hood a class is just a function with a prototype. One detail differs: a manually added method is enumerable, while methods from the class body are not.
Static method
Such a method is called directly on the class, not on an instance:
class MathUtils {
static square(n) {
return n * n;
}
}
console.log(MathUtils.square(5)); // 25Instances (
new MathUtils()) have no access tostaticmethods.
Getter and setter
They are used to "disguise" properties as methods and to control access to them:
class Product {
constructor(price) {
this._price = price;
}
get price() {
return this._price + " USD";
}
set price(value) {
if (value < 0) throw new Error("Price cannot be negative");
this._price = value;
}
}
const p = new Product(1000);
console.log(p.price); // "1000 USD"
p.price = 1500;
console.log(p.price); // "1500 USD"Reading
p.priceactually callsget price(), and assigningp.price = ...callsset price().
Private methods and arrow method fields
Private methods with a hash are unreachable from outside, only from inside the class:
class Account {
#logBalance() {
console.log(`Current balance: ${this.balance}`);
}
constructor(balance) {
this.balance = balance;
this.#logBalance(); // can only be called from here
}
}
const acc = new Account(500);
// acc.#logBalance(); -> error, this is a private methodIf you need a method to keep its this context, declare it as a field holding an arrow function:
class Button {
label = "Click me";
handleClick = () => {
console.log(`Clicked: ${this.label}`);
};
}
const b = new Button();
b.handleClick(); // Clicked: Click meSuch methods do not lose this even when passed around as a callback:
setTimeout(b.handleClick, 1000); // everything works correctlyThe price for that: an arrow field is created separately for every instance and does not live on the prototype.
Summary table
| Method kind | Syntax | Accessibility | Notes |
|---|---|---|---|
| Ordinary | method() {} | Through an instance | Shared by all instances |
| Static | static method() {} | Through the class itself | Not visible on instances |
| Getter / setter | get name() {}, set name(v) {} | Like a property | Lets you control access |
| Private | #method() {} | Only inside the class | Full encapsulation |
| Method field (arrow) | method = () => {} | Through an instance | Does not lose this |
Common mistakes
- Putting a comma or a semicolon between methods in the class body: this is not an object literal, no separators are needed there.
- Making every method an arrow field "so you never think about
this": that gives each instance its own copies of the functions and throws away the prototype benefit. - Calling a
staticmethod on an instance and getting aTypeError. - Naming a getter exactly like a field (
get price()plusthis.price = ...in the constructor): that gives infinite recursion, which is why the example names the field_price. - Forgetting that
#methods are invisible even in subclasses: class privacy works per class, not along the prototype chain.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.