Skip to content
JavaScript js objects 4 min read

Methods & this

Objects are more than passive bags of data — they can also carry behavior. A method is simply a function stored as a property, and inside that method the this keyword gives you a way to reach back to the object the method was called on. Understanding how this is resolved is one of the most important (and most misunderstood) parts of JavaScript, because it is decided at call time, not where the function is written.

Functions as properties

Any value can live in an object, including a function. When a function is a property of an object, we call it a method.

const user = {
  firstName: "Ada",
  lastName: "Lovelace",
  greet: function () {
    return "Hello!";
  },
};

console.log(user.greet());

Output:

Hello!

The function is invoked with user.greet(). Because it is reached through user, that object becomes the method’s receiver — and that is exactly what this will point to.

Shorthand method syntax

The classic key: function () {} form is verbose. ES2015 introduced a cleaner method shorthand: drop the colon and the function keyword entirely.

const user = {
  firstName: "Ada",
  lastName: "Lovelace",
  // shorthand method
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
};

console.log(user.fullName());

Output:

Ada Lovelace

The shorthand is the modern default. It reads better and is what you will see in nearly all current codebases.

How this works inside methods

Inside a regular (non-arrow) method, this refers to the object the method was called on — the value to the left of the dot at the call site. It is bound dynamically each time the method runs.

const account = {
  owner: "Grace",
  balance: 100,
  deposit(amount) {
    this.balance += amount;
    return `${this.owner} now has $${this.balance}`;
  },
};

console.log(account.deposit(50));

Output:

Grace now has $150

Because this is resolved at call time, the same function can serve different objects:

function describe() {
  return `${this.name} (${this.role})`;
}

const dev = { name: "Lin", role: "engineer", describe };
const pm = { name: "Omar", role: "manager", describe };

console.log(dev.describe());
console.log(pm.describe());

Output:

Lin (engineer)
Omar (manager)

Gotcha: If you pull a method off its object and call it bare — const fn = dev.describe; fn(); — there is no object to the left of the dot. In strict mode (and inside ES modules) this becomes undefined, so this.name throws. Keep the call attached to its receiver, or bind it explicitly.

The arrow-function-as-method pitfall

Arrow functions do not have their own this. They capture this from the surrounding (lexical) scope at the moment they are defined. That makes them a poor choice for top-level methods, because the surrounding scope is usually the module or global, not the object.

const counter = {
  count: 0,
  // BAD: arrow captures outer this, not `counter`
  increment: () => {
    this.count++;
    return this.count;
  },
};

console.log(counter.increment());

Output:

NaN

Here this is not counter, so this.count is undefined, and undefined++ produces NaN. Use a regular method instead:

const counter = {
  count: 0,
  increment() {
    this.count += 1;
    return this.count;
  },
};

console.log(counter.increment());
console.log(counter.increment());

Output:

1
2

Where arrows do shine is inside a method, for callbacks that should keep the outer this:

const timer = {
  label: "build",
  steps: ["compile", "bundle", "minify"],
  run() {
    this.steps.forEach((step) => {
      // arrow keeps `this` === timer
      console.log(`[${this.label}] ${step}`);
    });
  },
};

timer.run();

Output:

[build] compile
[build] bundle
[build] minify

If forEach used a regular function (step) {} callback, its this would be undefined and this.label would throw — the arrow neatly avoids that.

Method definitions compared

FormHas own this?this at callGood for
key: function () {}YesThe receiver (object before the dot)Methods (legacy syntax)
key() {} (shorthand)YesThe receiverMethods (preferred)
key: () => {}NoInherited from enclosing scopeCallbacks inside methods, not top-level methods

Best Practices

  • Use method shorthand (name() {}) for defining object methods — it is concise and binds this correctly.
  • Never use an arrow function as a top-level method when you need this to reference the object.
  • Do use arrow functions for inner callbacks (in forEach, map, setTimeout) when you want to preserve the surrounding this.
  • Remember this is determined by how a function is called, not where it is defined.
  • When passing a method as a callback, preserve its receiver with obj.method.bind(obj) or wrap it in an arrow: () => obj.method().
  • Keep method calls attached to their object; under strict mode a detached call leaves this as undefined.
Last updated June 1, 2026
Was this helpful?