JavaScript Object Methods

Methods are functions that belong to objects. They allow objects to have behavior, not just data. Understanding methods and the this keyword is essential for working with JavaScript objects.

Try It Yourself

Experiment with object methods and the this keyword:

index.js
// Object Methods and 'this' keyword

const person = {
  firstName: "Alice",
  lastName: "Smith",
  age: 28,

  // Method using 'this' to access object properties
  getFullName() {
    return this.firstName + " " + this.lastName;
  },

  // Method that modifies object state
  haveBirthday() {
    this.age++;
    console.log("Happy birthday! Now " + this.age + " years old.");
  },

  // Method with parameters
  greet(greeting) {
    return greeting + ", I'm " + this.getFullName() + "!";
  }
};

// Calling methods
console.log(person.getFullName());
console.log(person.greet("Hello"));

person.haveBirthday();
console.log("Current age:", person.age);

Defining Methods

A method is a function stored as an object property. You can define methods in several ways:

javascript
const calculator = {
  value: 0,

  // Method defined with function keyword
  add: function(n) {
    this.value += n;
    return this; // Enable chaining
  },

  // ES6 shorthand method (preferred)
  subtract(n) {
    this.value -= n;
    return this;
  },

  reset() {
    this.value = 0;
    return this;
  },

  getValue() {
    return this.value;
  }
};

// Using the methods
calculator.add(10).subtract(3).add(5);
console.log(calculator.getValue()); // 12
Use Method Shorthand
The ES6 shorthand syntax (methodName() {}) is cleaner and is the preferred way to define methods in modern JavaScript.

The this Keyword

Inside a method, this refers to the object that owns the method. It allows the method to access other properties and methods of the same object:

javascript
const user = {
  name: "Alice",
  age: 25,

  // 'this' refers to the object calling the method
  introduce() {
    console.log("Hi, I'm " + this.name);
    console.log("I'm " + this.age + " years old");
  },

  // Methods can call other methods via 'this'
  celebrateBirthday() {
    this.age++;
    this.introduce();
  }
};

user.introduce();
// Hi, I'm Alice
// I'm 25 years old

user.celebrateBirthday();
// Hi, I'm Alice
// I'm 26 years old

Losing this Context

Be careful when extracting methods from objects or using them as callbacks - you can lose the this context:

javascript
const person = {
  name: "Alice",
  greet() {
    console.log("Hello, " + this.name);
  }
};

// Normal method call - 'this' is the object
person.greet(); // "Hello, Alice"

// Extracting the method loses 'this' context!
const greetFunc = person.greet;
greetFunc(); // "Hello, undefined" (this is now global/undefined)

// Fix with bind()
const boundGreet = person.greet.bind(person);
boundGreet(); // "Hello, Alice"
Common Pitfall
When you assign a method to a variable or pass it as a callback, it loses its connection to the original object. Use bind() or arrow functions to preserve the context.

Arrow Functions and this

Arrow functions do NOT have their own this binding. They inherit this from the enclosing scope:

javascript
const person = {
  name: "Alice",

  // Regular method - works correctly
  greetRegular() {
    console.log("Regular: Hello, " + this.name);
  },

  // Arrow function - 'this' is NOT the object!
  greetArrow: () => {
    console.log("Arrow: Hello, " + this.name); // undefined!
  }
};

person.greetRegular(); // "Regular: Hello, Alice"
person.greetArrow();   // "Arrow: Hello, undefined"

// Arrow functions inherit 'this' from enclosing scope
// In this case, 'this' is the global object (or undefined in strict mode)

When Arrow Functions ARE Useful

Arrow functions are perfect for callbacks inside methods, where you want to preserve the outer this:

javascript
const team = {
  name: "Development",
  members: ["Alice", "Bob", "Carol"],

  // Arrow functions are USEFUL in callbacks
  printMembers() {
    console.log("Team: " + this.name);

    // Arrow function preserves 'this' from printMembers
    this.members.forEach((member) => {
      console.log(this.name + " member: " + member);
    });
  },

  // Compare with regular function callback
  printMembersBroken() {
    console.log("Team: " + this.name);

    // Regular function has its own 'this' (undefined in strict mode)
    this.members.forEach(function(member) {
      // console.log(this.name + " member: " + member); // Error!
    });
  }
};

team.printMembers();
// Team: Development
// Development member: Alice
// Development member: Bob
// Development member: Carol
FeatureRegular FunctionArrow Function
Own this binding
Good for methods
Good for callbacksNeeds binding
Can use bind/call/applyNo effect
Syntaxfunction() {}() => {}

call(), apply(), and bind()

These methods let you explicitly set what this refers to:

javascript
const person = {
  name: "Alice"
};

function greet(greeting, punctuation) {
  console.log(greeting + ", " + this.name + punctuation);
}

// call() - invoke with explicit 'this' and individual args
greet.call(person, "Hello", "!");
// "Hello, Alice!"

// apply() - same as call(), but args as array
greet.apply(person, ["Hi", "?"]);
// "Hi, Alice?"

// bind() - returns new function with 'this' permanently bound
const greetAlice = greet.bind(person);
greetAlice("Hey", ".");
// "Hey, Alice."

// bind() with preset arguments (partial application)
const sayHiToAlice = greet.bind(person, "Hi");
sayHiToAlice("!!!");
// "Hi, Alice!!!"
When to Use Each
  • call() - Invoke immediately with individual arguments
  • apply() - Invoke immediately with array of arguments
  • bind() - Create a new function for later use

Getters and Setters

Getters and setters let you define computed properties that look like regular properties but execute code:

javascript
const circle = {
  radius: 5,

  // Getter - accessed like a property, but runs a function
  get diameter() {
    return this.radius * 2;
  },

  get area() {
    return Math.PI * this.radius ** 2;
  },

  // Setter - assigned like a property, but runs a function
  set diameter(value) {
    this.radius = value / 2;
  }
};

// Using getters (no parentheses)
console.log(circle.diameter); // 10
console.log(circle.area);     // 78.54...

// Using setter
circle.diameter = 20;
console.log(circle.radius);   // 10

Method Chaining

Return this from methods to enable chaining multiple method calls:

javascript
const queryBuilder = {
  query: "",

  select(fields) {
    this.query += "SELECT " + fields + " ";
    return this; // Return 'this' to enable chaining
  },

  from(table) {
    this.query += "FROM " + table + " ";
    return this;
  },

  where(condition) {
    this.query += "WHERE " + condition + " ";
    return this;
  },

  build() {
    return this.query.trim();
  }
};

// Method chaining in action
const sql = queryBuilder
  .select("name, email")
  .from("users")
  .where("age > 18")
  .build();

console.log(sql);
// "SELECT name, email FROM users WHERE age > 18"

Private Methods

You can create private methods using closures or ES2022 private fields:

javascript
// Using closures for private methods (pre-ES2022)
function createCounter() {
  let count = 0; // Private variable

  function increment() { // Private method
    count++;
  }

  return {
    add() {
      increment();
      return this;
    },
    getValue() {
      return count;
    }
  };
}

const counter = createCounter();
counter.add().add().add();
console.log(counter.getValue()); // 3
// counter.increment(); // Error - not accessible

// ES2022 private fields and methods
class ModernCounter {
  #count = 0; // Private field

  #increment() { // Private method
    this.#count++;
  }

  add() {
    this.#increment();
    return this;
  }

  getValue() {
    return this.#count;
  }
}

Test Your Knowledge

Object Methods Quiz

5 questions
Question 1

What does "this" refer to inside an object method?

Question 2

Why should you NOT use arrow functions as object methods?

Question 3

What is the difference between call() and apply()?

Question 4

What does bind() return?

Question 5

How do you define a getter in an object?

Coding Challenge

Create a Bank Account
Medium

Build a bank account factory function with deposit, withdraw, getBalance, and transferTo methods. Methods should be chainable where appropriate.

Starter Code
// Challenge: Create a Bank Account object with methods
// Requirements:
// - deposit(amount) - add money, return the object for chaining
// - withdraw(amount) - remove money if sufficient funds, return object
// - getBalance() - return current balance
// - transferTo(otherAccount, amount) - transfer to another account

const createBankAccount = (initialBalance = 0) => {
  // Your code here
};

// Test your implementation
const alice = createBankAccount(100);
const bob = createBankAccount(50);

alice.deposit(50).withdraw(30);
console.log("Alice balance:", alice.getBalance()); // 120

bob.deposit(100);
console.log("Bob balance:", bob.getBalance()); // 150

alice.transferTo(bob, 40);
console.log("Alice after transfer:", alice.getBalance()); // 80
console.log("Bob after transfer:", bob.getBalance()); // 190

Summary

  • Methods are functions stored as object properties
  • this refers to the object that owns the method
  • Arrow functions don't have their own this - avoid them for object methods
  • Use bind() to permanently bind this to a function
  • call() and apply() invoke functions with a specified this
  • Getters and setters create computed properties
  • Return this from methods to enable method chaining