Docs

README

7.2 Object Methods

Table of Contents

  1. •Introduction
  2. •Defining Methods
  3. •Method Shorthand Syntax
  4. •The this Keyword in Methods
  5. •Method Invocation Patterns
  6. •Arrow Functions as Methods
  7. •Chaining Methods
  8. •Computed Method Names
  9. •Best Practices
  10. •Common Pitfalls

Introduction

Object methods are functions stored as object properties. They allow objects to have behavior, not just data.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│              Object Anatomy                  │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                             │
│  const user = {                             │
│    name: "Alice",        ← Property (data)  │
│    age: 30,              ← Property (data)  │
│    greet() { ... }       ← Method (behavior)│
│  };                                         │
│                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Why Methods Matter:

  • •Encapsulate behavior with related data
  • •Enable object-oriented programming patterns
  • •Provide clean APIs for interacting with objects
  • •Allow objects to manipulate their own state

Defining Methods

Traditional Function Expression

const calculator = {
  value: 0,

  add: function (num) {
    this.value += num;
    return this;
  },

  subtract: function (num) {
    this.value -= num;
    return this;
  },
};

ES6 Method Shorthand (Preferred)

const calculator = {
  value: 0,

  add(num) {
    this.value += num;
    return this;
  },

  subtract(num) {
    this.value -= num;
    return this;
  },
};

Method Shorthand Syntax

ES6 introduced a cleaner way to define methods:

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│            Method Definition Comparison                 │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│     Traditional (ES5)    │      Shorthand (ES6+)       │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│  method: function() {    │  method() {                 │
│    // code               │    // code                  │
│  }                       │  }                          │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│  getName: function() {   │  getName() {                │
│    return this.name;     │    return this.name;        │
│  }                       │  }                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Benefits of Shorthand

FeatureTraditionalShorthand
SyntaxVerboseConcise
ReadabilityGoodBetter
super accessNoYes
Use as constructorYesNo
[[HomeObject]]NoYes

The this Keyword in Methods

The this keyword refers to the object that is executing the current method.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│               How `this` Works in Methods                │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                         │
│  const user = {                                         │
│    name: "Bob",                                         │
│    greet() {                                            │
│      console.log(`Hello, ${this.name}`);                │
│    }              ↑                                     │
│  };               │                                     │
│                   │                                     │
│  user.greet();ā”€ā”€ā”€ā”€ā”˜ `this` refers to `user`             │
│                                                         │
│  Output: "Hello, Bob"                                   │
│                                                         │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

this is Determined at Call Time

const person = {
  name: 'Alice',
  greet() {
    console.log(`Hi, I'm ${this.name}`);
  },
};

person.greet(); // Hi, I'm Alice

// Extract the method
const greetFn = person.greet;
greetFn(); // Hi, I'm undefined (lost `this` context!)

Method Invocation Patterns

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                 Method Invocation Patterns                   │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│  1. Object Method Invocation                                │
│     obj.method()  →  this = obj                             │
│                                                             │
│  2. Explicit Binding                                        │
│     method.call(obj)   →  this = obj                        │
│     method.apply(obj)  →  this = obj                        │
│     method.bind(obj)() →  this = obj                        │
│                                                             │
│  3. Implicit Binding (standalone call)                      │
│     method()  →  this = undefined (strict mode)             │
│                  this = window (non-strict)                 │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Example: Different Invocation Patterns

const obj = {
  name: 'MyObject',
  show() {
    console.log(this?.name || 'No context');
  },
};

// 1. Method invocation
obj.show(); // "MyObject"

// 2. Explicit binding
const standalone = obj.show;
standalone.call(obj); // "MyObject"
standalone.apply(obj); // "MyObject"
const bound = standalone.bind(obj);
bound(); // "MyObject"

// 3. Lost context
standalone(); // "No context" (undefined in strict mode)

Arrow Functions as Methods

āš ļø Warning: Arrow functions don't have their own this!

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│         Arrow Functions vs Regular Methods                   │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│  Regular Method:                                            │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”                    │
│  │ const obj = {                       │                    │
│  │   name: "Object",                   │                    │
│  │   greet() {                         │                    │
│  │     console.log(this.name); āœ“       │                    │
│  │   }                                 │                    │
│  │ };                                  │                    │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜                    │
│                                                             │
│  Arrow Function (DON'T DO THIS):                            │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”                    │
│  │ const obj = {                       │                    │
│  │   name: "Object",                   │                    │
│  │   greet: () => {                    │                    │
│  │     console.log(this.name); āœ—       │                    │
│  │   }  // `this` is from outer scope! │                    │
│  │ };                                  │                    │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜                    │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

When Arrow Functions ARE Useful in Objects

const counter = {
  count: 0,

  // Regular method
  start() {
    // Arrow function INSIDE method preserves `this`
    setInterval(() => {
      this.count++;
      console.log(this.count);
    }, 1000);
  },
};

counter.start(); // Works correctly!

Chaining Methods

Method chaining allows multiple method calls in a single statement by returning this.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    Method Chaining Pattern                      │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                │
│  builder.setName("Product")                                    │
│         .setPrice(99.99)    ← Each method returns `this`       │
│         .setCategory("Tech")                                   │
│         .build();                                              │
│                                                                │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │
│  │                     Flow Diagram                          │  │
│  │                                                          │  │
│  │  builder → setName() → returns builder                   │  │
│  │          → setPrice() → returns builder                  │  │
│  │          → setCategory() → returns builder               │  │
│  │          → build() → returns final product               │  │
│  │                                                          │  │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │
│                                                                │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Implementing Method Chaining

const queryBuilder = {
  query: '',

  select(fields) {
    this.query += `SELECT ${fields} `;
    return this; // Return this for chaining
  },

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

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

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

const sql = queryBuilder.select('*').from('users').where('age > 18').build();

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

Computed Method Names

ES6 allows using expressions as method names:

const prefix = 'get';
const property = 'Name';

const obj = {
  name: 'Dynamic',

  // Computed method name
  [`${prefix}${property}`]() {
    return this.name;
  },

  [`set${property}`](value) {
    this.name = value;
  },
};

console.log(obj.getName()); // "Dynamic"
obj.setName('Updated');
console.log(obj.getName()); // "Updated"

Use Cases for Computed Method Names

// Creating methods from an array
const operations = ['add', 'subtract', 'multiply', 'divide'];

const mathOps = {
  value: 10,

  ...operations.reduce((acc, op) => {
    acc[`${op}By`] = function (n) {
      switch (op) {
        case 'add':
          this.value += n;
          break;
        case 'subtract':
          this.value -= n;
          break;
        case 'multiply':
          this.value *= n;
          break;
        case 'divide':
          this.value /= n;
          break;
      }
      return this;
    };
    return acc;
  }, {}),
};

mathOps.addBy(5).multiplyBy(2).subtractBy(10);
console.log(mathOps.value); // 20

Best Practices

1. Use Method Shorthand

// āœ“ Good
const obj = {
  method() {},
};

// āœ— Avoid
const obj = {
  method: function () {},
};

2. Keep Methods Focused

// āœ“ Good - Single responsibility
const user = {
  validate() {
    /* validation only */
  },
  save() {
    /* saving only */
  },
  notify() {
    /* notification only */
  },
};

// āœ— Avoid - Method doing too much
const user = {
  validateAndSaveAndNotify() {
    /* everything */
  },
};

3. Return Meaningful Values

// āœ“ Good - Return this for chaining, or useful value
const cart = {
  items: [],
  add(item) {
    this.items.push(item);
    return this; // Enable chaining
  },
  getTotal() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  },
};

4. Handle Missing Context

const obj = {
  name: 'Object',
  greet() {
    // Defensive: handle potential undefined this
    if (!this || !this.name) {
      console.log('Hello, anonymous');
      return;
    }
    console.log(`Hello, ${this.name}`);
  },
};

Common Pitfalls

1. Losing this Context

const user = {
  name: 'Alice',
  greet() {
    console.log(`Hello, ${this.name}`);
  },
};

// āœ— Problem: Lost context
const greet = user.greet;
greet(); // undefined

// āœ“ Solution 1: Use bind
const boundGreet = user.greet.bind(user);
boundGreet(); // "Hello, Alice"

// āœ“ Solution 2: Arrow function wrapper
const arrowGreet = () => user.greet();
arrowGreet(); // "Hello, Alice"

2. Arrow Functions as Methods

// āœ— Don't use arrow functions for methods
const obj = {
  value: 42,
  getValue: () => this.value, // Wrong! `this` is not obj
};

// āœ“ Use regular methods
const obj = {
  value: 42,
  getValue() {
    return this.value;
  },
};

3. Callback Context Loss

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

  // āœ— Problem
  startBad() {
    setTimeout(this.increment, 1000); // Loses context
  },

  // āœ“ Solution 1: Bind
  startWithBind() {
    setTimeout(this.increment.bind(this), 1000);
  },

  // āœ“ Solution 2: Arrow function
  startWithArrow() {
    setTimeout(() => this.increment(), 1000);
  },
};

4. Nested Method Definitions

const obj = {
  outer() {
    // āœ— Problem: `this` changes in nested function
    function inner() {
      console.log(this); // undefined or window
    }
    inner();

    // āœ“ Solution: Arrow function or save reference
    const that = this;
    function innerFixed() {
      console.log(that); // obj
    }

    const arrowInner = () => {
      console.log(this); // obj
    };
  },
};

Method Types Summary

TypeSyntaxthis BindingUse Case
Shorthandmethod() {}DynamicDefault choice
Traditionalmethod: function() {}DynamicLegacy code
Arrowmethod: () => {}LexicalNever for methods
Getterget prop() {}DynamicComputed properties
Setterset prop(v) {}DynamicProperty validation

Files in This Section

  • •README.md - This documentation
  • •examples.js - Runnable code examples
  • •exercises.js - Practice exercises with solutions

Navigation

README - JavaScript Tutorial | DeepML