JavaScript Object Methods
An object method is simply a function that lives inside an object as one of its properties. Instead of writing standalone functions that take an object as an argument, you attach behavior directly to the data it operates on, so car.describe() reads more naturally than describe(car). Methods are the foundation of object-oriented patterns in JavaScript, and understanding exactly how they work — especially how the this keyword is bound — will save you from some of the most common bugs in the language.
Overview: What Is an Object Method?
In JavaScript, functions are values. Because a function is just a value, you can store one as the value of an object property, exactly like you would store a string or a number. When a property’s value happens to be a function, and you access it through the object (for example obj.doSomething()), we call that property a method rather than just a property.
The key idea that separates a method from an ordinary function is how it is called, not how it is defined. JavaScript does not have a special “method” data type — under the hood a method is a normal function reference sitting in a property slot. What makes it behave like a method is the call site: when you write obj.doSomething(), JavaScript evaluates obj.doSomething to get the function, then calls it with obj automatically passed in as the special this value for that call. This is why the same function can behave differently depending on how it is invoked — this is not fixed at definition time, it is determined at call time by the object to the left of the dot (or bracket).
This matters because it means a method can read and modify the object it belongs to using this, without needing to know the object’s name in advance. That is what lets you copy an object, pass it around, or create many similar objects, and have each one’s methods automatically operate on the correct instance.
Syntax
There are several ways to attach a function to an object as a method. All of them end up storing a function as a property value, but the syntax and a few subtle behaviors differ.
| Style | Example | Notes |
|---|---|---|
| Function expression | greet: function () { ... } |
Classic pre-ES6 syntax. Still valid, more verbose. |
| Method shorthand (ES6) | greet() { ... } |
Preferred modern syntax. Behaves like a function expression, but slightly leaner and marked internally as a method. |
| Computed method name | [dynamicKey]() { ... } |
The property name is computed from an expression at object-creation time. |
| Async method | async load() { ... } |
Method that returns a Promise; use await inside it. |
| Generator method | *iterate() { ... } |
Method that returns an iterator via yield. |
| Arrow function property | greet: () => { ... } |
Technically stores a function, but it does not get its own this — avoid for methods that need this. |
In practice, the ES6 method shorthand is what you should reach for by default: propertyName(parameters) { statements }. It omits the function keyword and the colon entirely.
Examples
Example 1: A basic method using this
const car = {
brand: "Toyota",
model: "Corolla",
year: 2022,
describe: function () {
return `${this.year} ${this.brand} ${this.model}`;
}
};
console.log(car.describe());
Output:
2022 Toyota Corolla
The describe property holds a function. Because we call it as car.describe(), JavaScript binds this to car for the duration of that call, so this.year, this.brand, and this.model resolve to the matching properties on car.
Example 2: Method shorthand and mutating state
const counter = {
count: 0,
increment() {
this.count += 1;
return this.count;
},
reset() {
this.count = 0;
}
};
console.log(counter.increment());
console.log(counter.increment());
counter.reset();
console.log(counter.count);
Output:
1
2
0
This uses the ES6 shorthand: increment() is equivalent to increment: function () { ... }. Each call to counter.increment() mutates counter.count through this, and the change is visible afterward because this refers to the actual counter object, not a copy of it.
Example 3: A computed method name calling another method
const key = "getFullName";
const user = {
firstName: "Ada",
lastName: "Lovelace",
[key]() {
return `${this.firstName} ${this.lastName}`;
},
greet() {
return `Hello, ${this.getFullName()}!`;
}
};
console.log(user.getFullName());
console.log(user.greet());
Output:
Ada Lovelace
Hello, Ada Lovelace!
The square brackets [key]() tell JavaScript to use the value of the key variable (the string "getFullName") as the property name, so this defines a method literally named getFullName. Notice that greet calls this.getFullName() — one method calling a sibling method through this is an extremely common pattern for composing behavior inside an object.
Under the Hood: How this Gets Bound
It is worth being precise about what actually happens when JavaScript executes obj.method():
- The engine evaluates the expression before the parentheses,
obj.method, which looks up themethodproperty and produces a reference to the function, remembering that it was fetched fromobj. - Because the call uses that member-access reference, JavaScript performs what the specification calls a “method call”: it invokes the function and sets
thisinside the function body toobjfor that single invocation. - Once the function starts running,
thisbehaves like any other local binding for the lifetime of that call — it is not re-evaluated line by line, it was fixed the moment the call happened.
Crucially, this binding is a property of how the function is called, not where it was defined or which object it “belongs” to conceptually. If you detach a method from its object and call it without a receiver, it loses that binding entirely:
"use strict";
const wallet = {
balance: 100,
getBalance() {
return this.balance;
}
};
console.log(wallet.getBalance());
const getBalance = wallet.getBalance;
try {
console.log(getBalance());
} catch (error) {
console.log(error.message);
}
Output:
100
Cannot read properties of undefined (reading 'balance')
The first call, wallet.getBalance(), is a proper method call, so this is wallet and it returns 100. The second call takes the same function but invokes it with no object in front of the dot, so in strict mode this is undefined, and reading .balance off undefined throws a TypeError. In non-strict (sloppy) code the same call would silently set this to the global object instead of throwing, which is arguably worse because it fails quietly.
Common Mistakes
Mistake 1: Using an arrow function as a method
Arrow functions do not have their own this. They capture this lexically from the surrounding scope where they were defined, not from how they are called. That makes them a poor fit for object methods that need to read the object’s own properties.
const timer = {
seconds: 0,
start: () => {
console.log(this.seconds);
}
};
timer.start();
Output:
undefined
Because start is an arrow function, its this is whatever this was in the scope surrounding the object literal (at the top of a Node module, that is module.exports, an object with no seconds property) — it is never rebound to timer, no matter how you call timer.start(). The fix is to use a regular method (shorthand or function expression) so this is bound by the call site instead:
const timer2 = {
seconds: 0,
start() {
console.log(this.seconds);
}
};
timer2.start();
Output:
0
Mistake 2: Passing a method reference around and losing its receiver
Whenever you extract a method off an object — assign it to a variable, pass it as a callback to setTimeout, an event listener, or .map() — you strip away the object that used to sit before the dot. The function itself remembers nothing about where it came from, so this will not automatically be the original object anymore (see the wallet.getBalance example above). The standard fix is Function.prototype.bind(), which permanently locks this to a chosen object:
const wallet3 = {
balance: 500,
getBalance() {
return this.balance;
}
};
const bound = wallet3.getBalance.bind(wallet3);
console.log(bound());
Output:
500
bind() returns a new function that always calls the original with this set to wallet3, no matter how the new function is later invoked — so it is safe to hand off to a callback or store separately.
Best Practices
- Prefer ES6 method shorthand (
name() { ... }) overname: function () { ... }— it is shorter and clearly signals “this is a method”. - Never use an arrow function for a method body that needs to read or mutate the object’s own properties via
this. - When passing a method as a callback (event handlers,
setTimeout, array callbacks), either wrap it in an arrow function at the call site (() => obj.method()) or pre-bind it with.bind(obj). - Keep methods focused on one responsibility; have methods call sibling methods through
thisinstead of duplicating logic. - Use computed method names (
[expr]() {}) sparingly, only when the method name genuinely needs to be dynamic — a hard-coded name is easier to search for and read. - Reach for
classsyntax once an object has many methods and you need multiple similar instances; classes are effectively method-storage on a shared prototype, so understanding plain-object methods first makes classes much easier to learn. - Add
"use strict";(or use ES modules, which are strict by default) so that calling a method without a receiver fails loudly with aTypeErrorinstead of silently touching the global object.
Practice Exercises
- Create an object literal called
rectanglewithwidthandheightproperties, plus a methodarea()that returnswidth * heightusingthis. Log the result of calling it. - Take the
counterobject from Example 2 and add a new methoddecrement()that lowerscountby 1 and returns the new value. Then callincrement()twice anddecrement()once, and predict the final value ofcounter.countbefore checking it. - Given
const person = { name: "Sam", sayHi() { console.log(this.name); } };, extractsayHiinto a standalone variable and call it directly. Explain in your own words why the output differs from callingperson.sayHi(), then fix it usingbind()so the standalone call also logs"Sam".
Summary
- An object method is a function stored as a property value; what makes it act like a method is being called through the object (
obj.method()), which bindsthisto that object for the call. - ES6 method shorthand (
name() { ... }) is the modern, preferred way to define methods on object literals. thisis determined at call time by the call site, not at function-definition time — extracting a method and calling it without a receiver loses the intendedthis.- Arrow functions capture
thislexically from their enclosing scope and should not be used as object methods that rely onthis. .bind(obj)creates a new function permanently locked to a giventhis, which is the standard fix when passing methods as callbacks.- Methods can call other methods on the same object through
this, letting you compose behavior cleanly inside a single object.
