The previous lesson left a question hanging: if an object can hold numbers, strings and arrays, why not functions? The answer is that it can, and that changes the way code is organized. A function stored in a property is called a method, and it lets the Taller Nómada board be more than a bag of tasks: an object that knows how to add a task, change its status and compute its own summary. Methods bring this along with them, the JavaScript keyword with the worst reputation: not because it is complicated, but because almost everyone learns it with the wrong rule. In this lesson you will learn it with the right one —this depends on how the function is called, not on where it is defined—, you will see the four call forms in a single table, you will solve the classic problem of losing this when passing a method as a callback, and you will finally settle the debt 03-02 left about arrow functions.

Contents

  1. From standalone function to method
  2. The shorthand method syntax
  3. What this is and the fundamental rule
  4. The four call forms
  5. call, apply and bind
  6. The Taller Nómada board object
  7. The classic problem: losing this in a callback
  8. The three solutions
  9. Why an arrow function has no this of its own
  10. this in loops and nested functions
  11. When the arrow helps and when it gets in the way
  12. Common Mistakes and Tips
  13. Exercises
  14. Conclusion

  1. From standalone function to method

We start from the previous lesson's backlog and from describeTask(task, today). Right now, the function and the data live apart:

const task = backlog[0];
console.log(describeTask(task, TODAY));

Nothing stops you from putting the function inside the object:

const task = {
  id: 6,
  title: 'Carpentry workshop quote',
  assignee: 'Iván',
  priority: 'high',
  status: 'pending',
  estimatedHours: 5,
  dueDate: '2026-09-05',

  // A property whose value is a function: that is a METHOD
  describe: function (today) {
    return `[${this.id}] ${this.title} · ${this.assignee} · ${this.estimatedHours} h`;
  }
};

console.log(task.describe('2026-09-20'));
// [6] Carpentry workshop quote · Iván · 5 h

Two new things in that block:

  • describe is a property like any other; the only special thing is that its value is a function. You can check it: typeof task.describe is 'function', and Object.keys(task) includes it.
  • Inside the method there is this, which in this call refers to the task object itself. Thanks to it, the method can read the data of "its" object without your passing it in as an argument.

The vocabulary difference is simple:

Term Definition Example
Function Named code invoked on its own priorityWeight('high')
Method A function stored in a property of an object task.describe('2026-09-20')
Receiver The object to the left of the dot in the call task

  1. The shorthand method syntax

Writing describe: function (today) { ... } works, but since ES2015 there is a shorter form and it is the one used today:

const board = {
  name: 'Taller Nómada',

  // Long syntax (still valid)
  greetLong: function () {
    return `Panel for ${this.name}`;
  },

  // Shorthand syntax: drop the colon and the word function
  greet() {
    return `Panel for ${this.name}`;
  }
};

console.log(board.greet());       // 'Panel for Taller Nómada'
console.log(board.greetLong());   // 'Panel for Taller Nómada'

Both do the same thing. The shorthand is shorter, more readable and it is the one you should use. Watch out for a third option that looks equivalent but is not:

const brokenBoard = {
  name: 'Taller Nómada',
  greet: () => `Panel for ${this.name}`     // ✗ arrow function
};

console.log(brokenBoard.greet());    // 'Panel for undefined'

That undefined is the subject of section 9. For now, hold on to the working rule: for the methods of an object literal, always use the shorthand syntax, never an arrow.

  1. What this is and the fundamental rule

this is a keyword that, inside a function, points to an object. Which object that is gets decided at the moment of the call, not at the moment of writing the function. This is the rule to memorize:

this does not depend on where the function is defined, but on HOW it is called.

It sounds abstract until you see one and the same function behave in three different ways:

'use strict';

function whoAmI() {
  return this;
}

const objectA = { name: 'A', whoAmI };
const objectB = { name: 'B', whoAmI };

console.log(objectA.whoAmI().name);   // 'A'
console.log(objectB.whoAmI().name);   // 'B'
console.log(whoAmI());                // undefined  (in strict mode)

It is the same function, defined only once. The only thing that changes is what sits to the left of the dot when calling it. With that mental picture, this stops being magic: it is simply "the object this was invoked on".

flowchart LR
    A["objectA.whoAmI()"] --> B["this = objectA"]
    C["objectB.whoAmI()"] --> D["this = objectB"]
    E["whoAmI()"] --> F["this = undefined<br/>(strict mode)"]
    G["whoAmI.call(objectA)"] --> H["this = objectA"]

  1. The four call forms

All the complexity of this boils down to this table. When you are unsure what this points to, work out which row you are in.

Call form How it is written Value of this
Plain call fn() undefined in strict mode; the global object (window/globalThis) without strict mode
As a method object.method() The receiver object (whatever is to the left of the dot)
With new new Constructor() The new object being created
With call/apply/bind fn.call(obj, ...) The object you specify explicitly

Let us take them one at a time.

Plain call. It is what you have been doing all through Module 3. In strict mode ('use strict', which you have been using since 01-04) this is undefined. Without strict mode it would be the global object, and that silent behavior is an inexhaustible source of bugs: that is why strict mode exists.

'use strict';

function report() {
  console.log(this);
}

report();          // undefined

As a method. The normal case: this is the receiver.

const team = {
  coordinator: 'Marta',
  introduce() {
    return `Coordinated by ${this.coordinator}`;
  }
};

console.log(team.introduce());   // 'Coordinated by Marta'

A warning is in order here: this is the object immediately to the left of the dot in the call, not the object where the method is written. If you move the method to another variable, everything changes (section 7).

With new. When you call a function with new, JavaScript creates an empty object, makes this point to it, runs the body and returns that object.

function Task(id, title) {
  this.id = id;
  this.title = title;
  this.status = 'pending';
}

const newTask = new Task(7, 'Service the paper guillotine');
console.log(newTask);   // Task { id: 7, title: 'Service the paper guillotine', status: 'pending' }

This is the mechanism constructors, prototypes and classes are built on, and it is studied in depth in Prototypes and Inheritance and Classes and OOP. Here you only need to know that it exists and that it is the third row of the table.

With call, apply or bind. That is the next section.

  1. call, apply and bind

Every JavaScript function comes with three built-in methods for deciding this by hand.

function describe(prefix, suffix) {
  return `${prefix}${this.title} (${this.estimatedHours} h)${suffix}`;
}

const task3 = { title: 'Update the bookings website', estimatedHours: 14 };

// call: this first, then the arguments LOOSE
console.log(describe.call(task3, '» ', ' ⏳'));
// » Update the bookings website (14 h) ⏳

// apply: this first, then the arguments in an ARRAY
console.log(describe.apply(task3, ['» ', ' ⏳']));
// » Update the bookings website (14 h) ⏳

// bind: does NOT call; it returns a NEW function with this fixed forever
const describeTask3 = describe.bind(task3);
console.log(describeTask3('· ', ''));
// · Update the bookings website (14 h)

A side-by-side summary:

Method Does it run now? How it passes the arguments What it returns
call(this, a, b) Yes Loose The function's result
apply(this, [a, b]) Yes In an array The function's result
bind(this, a) No Loose (they can be prefixed) A new function with this tied

A memory trick: call = commas, apply = array, bind = blocks (it fixes this and returns a function for later). In modern practice call and apply are rarely used —the spread of 04-07 replaces almost every use of apply— but bind is still essential, as you will see in section 8.

One surprising detail: bind is final. A function that is already bound cannot be rebound.

const boundToA = describe.bind({ title: 'A', estimatedHours: 1 });
const attempt = boundToA.bind({ title: 'B', estimatedHours: 2 });
console.log(attempt('', ''));   // 'A (1 h)'  ← the second bind has no effect

  1. The Taller Nómada board object

With this we can now build the centerpiece of the lesson: an object that holds the backlog and the operations on it. Marta would describe it like this: "the board is the list of tasks plus what you can do with it".

'use strict';

const TODAY = '2026-09-20';
const WEIGHTS = { high: 3, medium: 2, low: 1 };
const BADGES = { done: '✓', 'in-progress': '▸', pending: '○' };
const VALID_STATUSES = ['pending', 'in-progress', 'done'];

const board = {
  name: 'Taller Nómada',
  today: TODAY,
  tasks: [
    { id: 1, title: 'Redesign the multipurpose room',           assignee: 'Iván',  priority: 'high',   status: 'in-progress', estimatedHours: 12, dueDate: '2026-09-30' },
    { id: 2, title: 'Signage for the screen-printing workshop', assignee: 'Marta', priority: 'medium', status: 'pending',     estimatedHours: 6,  dueDate: '2026-10-15' },
    { id: 3, title: 'Update the bookings website',              assignee: 'Lucía', priority: 'high',   status: 'pending',     estimatedHours: 14, dueDate: '2026-10-02' },
    { id: 4, title: 'Screen-printing ink inventory',            assignee: 'Marta', priority: 'low',    status: 'done',        estimatedHours: 3,  dueDate: '2026-09-12' },
    { id: 5, title: 'Bookbinding guide for residents',          assignee: 'Iván',  priority: 'medium', status: 'in-progress', estimatedHours: 8,  dueDate: '2026-11-05' },
    { id: 6, title: 'Carpentry workshop quote',                 assignee: 'Iván',  priority: 'high',   status: 'pending',     estimatedHours: 5,  dueDate: '2026-09-05' }
  ],

  // ─── Query methods ──────────────────────────────────────────────
  findById(id) {
    for (const task of this.tasks) {
      if (task.id === id) return task;
    }
    return null;
  },

  // ─── Methods that modify the board ──────────────────────────────
  add(task) {
    if (this.findById(task.id) !== null) {
      throw new Error(`A task with id ${task.id} already exists.`);
    }
    this.tasks.push(task);
    return this;             // returning this allows calls to be chained
  },

  changeStatus(id, newStatus) {
    if (!VALID_STATUSES.includes(newStatus)) {
      throw new Error(`Invalid status: ${newStatus}`);
    }
    const task = this.findById(id);
    if (task === null) {
      throw new Error(`There is no task with id ${id}.`);
    }
    task.status = newStatus;
    return this;
  },

  // ─── Aggregation method ─────────────────────────────────────────
  summary() {
    let open = 0;
    let openHours = 0;
    let overdue = 0;
    let effort = 0;

    for (const task of this.tasks) {
      effort += (WEIGHTS[task.priority] ?? 0) * task.estimatedHours;
      if (task.status === 'done') continue;
      open++;
      openHours += task.estimatedHours;
      if (task.dueDate < this.today) overdue++;
    }

    return { total: this.tasks.length, open, openHours, overdue, effort };
  },

  line(task) {
    const badge = BADGES[task.status] ?? '?';
    const warning = task.dueDate < this.today && task.status !== 'done' ? ' ⚠ OVERDUE' : '';
    return `${badge} [${task.id}] ${task.title} · ${task.assignee} · ${task.estimatedHours} h${warning}`;
  }
};

console.log(board.summary());
// { total: 6, open: 5, openHours: 45, overdue: 1, effort: 124 }

console.log(board.line(board.findById(6)));
// ○ [6] Carpentry workshop quote · Iván · 5 h ⚠ OVERDUE

The numbers match the canonical backlog: 6 tasks, 5 open, 45 open hours, 1 overdue and a weighted effort of 124. Go over three design decisions in this object:

  1. this.tasks, not tasks. Inside a method, reaching the object's own properties means going through this. Writing tasks.push(...) would give a ReferenceError, because there is no variable with that name.
  2. The methods call each other through this. add uses this.findById(...). That is what lets you reuse logic without duplicating it.
  3. add and changeStatus return this. It is the fluent interface pattern: since every call returns the board itself, calls can be chained.
board
  .add({ id: 7, title: 'Service the bookbinding guillotine', assignee: 'Lucía',
         priority: 'medium', status: 'pending', estimatedHours: 2, dueDate: '2026-10-20' })
  .changeStatus(2, 'in-progress')
  .changeStatus(6, 'done');

console.log(board.summary());
// { total: 7, open: 5, openHours: 42, overdue: 0, effort: 128 }

And the errors use the throw new Error you learned in 02-05, so the caller can wrap it in try/catch:

try {
  board.changeStatus(99, 'done');
} catch (error) {
  console.log(`Could not update: ${error.message}`);
}
// Could not update: There is no task with id 99.

  1. The classic problem: losing this in a callback

Here is the moment when this bites. Recall the rule: this depends on how the function is called. If you pull a method out of its object, the call no longer has a receiver.

'use strict';

const summarize = board.summary;     // we extract the method into a variable
console.log(summarize());
// ✗ TypeError: Cannot read properties of undefined (reading 'tasks')

Why? Because summarize() is a plain call (the first row of the table): this is undefined, and this.tasks blows up. The function is the same; the call is not.

The truly frequent case is subtler, because it does not look as though you are extracting anything: it happens when you pass a method as a callback, something you will do constantly with the Module 6 events and with the array methods in the rest of this module.

const notifier = {
  recipient: 'Marta',
  notify(title) {
    console.log(`Notice for ${this.recipient}: ${title}`);
  }
};

notifier.notify('Carpentry workshop quote');
// Notice for Marta: Carpentry workshop quote   ✓

const titles = ['Carpentry workshop quote', 'Update the bookings website'];

titles.forEach(notifier.notify);
// ✗ TypeError: Cannot read properties of undefined (reading 'recipient')

What happens is that titles.forEach(notifier.notify) does not pass the method, it passes the function that was inside it. When forEach invokes it, it does so as a plain call, with no receiver whatsoever:

flowchart TD
    A["notifier.notify('x')"] --> B["Call WITH a receiver<br/>this = notifier ✓"]
    C["const f = notifier.notify<br/>f('x')"] --> D["Call WITHOUT a receiver<br/>this = undefined ✗"]
    E["forEach(notifier.notify)"] --> D

The correct mental picture: the dot is not part of the function. notifier.notify means "give me the value stored in the notify property". That value is an anonymous function that does not remember where it came from. The link with notifier exists only during the call with the dot.

  1. The three solutions

There are three ways to fix it, and it is worth knowing all three because you will meet them in other people's code.

Solution 1: bind. You tie the method to its object and pass the bound function.

titles.forEach(notifier.notify.bind(notifier));
// Notice for Marta: Carpentry workshop quote
// Notice for Marta: Update the bookings website

Solution 2: a wrapping arrow function. You call the method with the dot inside the arrow, so the call does have a receiver.

titles.forEach((title) => notifier.notify(title));

This is the most readable one and the one most often seen today. Notice that the key is not the arrow itself, but the fact that the invocation keeps the dot: notifier.notify(title).

Solution 3: an arrow method as an object field. You define the property with an arrow that captures this from the surrounding scope.

function createNotifier(recipient) {
  const obj = {
    recipient,
    notify: (title) => console.log(`Notice for ${obj.recipient}: ${title}`)
  };
  return obj;
}

const notice = createNotifier('Lucía');
titles.forEach(notice.notify);
// Notice for Lucía: Carpentry workshop quote
// Notice for Lucía: Update the bookings website

This third variant is the least common with object literals (it works better with classes, Module 5), but it illustrates a pattern you will see in many projects. A comparison:

Solution Advantages Drawbacks
bind Explicit; works with any function Verbose; creates a new function every time it runs
Wrapping arrow Short, clear, lets you reorder arguments One more level of nesting
Arrow method The method "never comes untied" Every object has its own copy of the function; it cannot be reused with call

And there is a fourth route specific to some array methods: forEach, map, filter and some accept a second argument with the this you want to use inside the callback.

const shortTasks = ['Request quotes', 'Visit two suppliers'];
shortTasks.forEach(notifier.notify, notifier);
// Notice for Marta: Request quotes
// Notice for Marta: Visit two suppliers

It works, but it is rarely used because it is easy to overlook when reading the code.

  1. Why an arrow function has no this of its own

In 03-02 we said that arrow functions behave differently with this and left the explanation for later. The moment has come.

An arrow function has no this of its own. Inside an arrow, this is exactly the same one that existed in the scope where the arrow was written. It is resolved like any other variable, following the scope chain you studied in 03-04.

This explains the failure in section 2:

'use strict';

const brokenBoard = {
  name: 'Taller Nómada',
  greet: () => `Panel for ${this.name}`
};

console.log(brokenBoard.greet());   // 'Panel for undefined'

The arrow was written outside any function, at module scope. There, this is not the object containing it (an object literal does not create a scope), but whatever there was at that level: undefined in a module, or module.exports in Node, or the global object in a classic script. In none of those cases is there a name property.

The table that sums up the difference:

Normal function (function, or shorthand method) Arrow function
this Decided at the call (the four forms) Inherited from the scope where it was written
Do call/apply/bind affect it? Yes No: they are ignored
Does it work as an object-literal method? Yes No
Does it work as a callback that needs the outer this? Not without bind Yes, perfectly
Does it have arguments? Yes No (use ...rest, 03-03)
Can it be used with new? Yes No

See for yourself:

const obj = { value: 42 };

const normalFn = function () { return this?.value; };
const arrowFn = () => this?.value;

console.log(normalFn.call(obj));   // 42        ← call works
console.log(arrowFn.call(obj));    // undefined ← call is ignored

  1. this in loops and nested functions

The case that confuses beginners most: a normal function nested inside a method. Since it is a plain call, it loses the method's this.

'use strict';

const report = {
  name: 'Taller Nómada',
  tasks: [{ title: 'Carpentry workshop quote' }, { title: 'Update the bookings website' }],

  printBadly() {
    this.tasks.forEach(function (task) {
      console.log(`${this.name}: ${task.title}`);   // ✗ this is undefined
    });
  }
};

// report.printBadly();
// ✗ TypeError: Cannot read properties of undefined (reading 'name')

printBadly does get its this right (it was called with the dot). But the anonymous function that forEach receives is invoked with no receiver, and its this has nothing to do with that of the method around it. Before arrows, this was solved with a trick you will see in old code:

printWithSelf() {
  const self = this;                    // the good this is saved into a normal variable
  this.tasks.forEach(function (task) {
    console.log(`${self.name}: ${task.title}`);
  });
}

With an arrow the trick is no longer needed, because the arrow inherits the this of the method where it was written:

const report = {
  name: 'Taller Nómada',
  tasks: [{ title: 'Carpentry workshop quote' }, { title: 'Update the bookings website' }],

  print() {
    this.tasks.forEach((task) => {
      console.log(`${this.name}: ${task.title}`);   // ✓ this = report
    });
  }
};

report.print();
// Taller Nómada: Carpentry workshop quote
// Taller Nómada: Update the bookings website

A classic for...of does not have this problem at all, because it introduces no new function:

printWithFor() {
  for (const task of this.tasks) {
    console.log(`${this.name}: ${task.title}`);   // ✓ same this as the method
  }
}

A derived rule, very practical: a loop does not change this; a function does. If the word function appears inside a method, be suspicious.

  1. When the arrow helps and when it gets in the way

With all of the above, the decision is mechanical:

Situation Choose Reason
Method of an object literal Shorthand method method() {} It needs the receiver as this
Callback inside a method Arrow function It inherits the correct this without bind
Callback that does not use this (map, filter, comparators) Arrow function Shorter; this is irrelevant
Event handler that needs the DOM element Normal function The browser puts the element in this (Module 6)
Constructor or function to be used with new Normal function Arrows do not support new
Standalone function unrelated to any object Either this plays no part

Applied to the board, the definitive version combines both: shorthand methods on the outside, arrows on the inside.

const finalBoard = {
  name: 'Taller Nómada',
  today: TODAY,
  tasks: board.tasks,

  overdueTitles() {
    return this.tasks
      .filter((t) => t.dueDate < this.today && t.status !== 'done')   // arrow: this = finalBoard
      .map((t) => t.title);
  }
};

console.log(finalBoard.overdueTitles());
// [ 'Carpentry workshop quote' ]

(The filter and map methods appearing there are studied in lessons 04-04 and 04-05; here they only matter as an example of callbacks inside a method.)

Common Mistakes and Tips

1. Forgetting this. inside a method.

summary() {
  for (const t of tasks) { }       // ✗ ReferenceError: tasks is not defined
  for (const t of this.tasks) { }  // ✓
}

2. Using an arrow as the method of an object literal. greet: () => this.name will never see the object. Use greet() { ... }.

3. Passing a method as a callback without tying it. array.forEach(obj.method) loses this. Write array.forEach((x) => obj.method(x)).

4. Believing that bind modifies the original function. It does not: it returns a new one. obj.method.bind(obj); on a line of its own does absolutely nothing; you have to store the result.

5. Calling bind inside a loop or a render. Each bind creates a different function. In Module 6 that means event handlers you cannot remove with removeEventListener because the reference is no longer the same. Bind once and keep the result.

6. Relying on this without strict mode. Without 'use strict', a plain call sets this to the global object and assignments to this.something silently create globals. This course has used strict mode since 01-04 precisely so that mistake turns into a visible TypeError.

Professional tip. When you do not know what this points to, do not reason: print it. A console.log(this) on the method's first line settles the question in two seconds, and in Module 8 you will learn to inspect it with a breakpoint without touching the code.

Exercises

Exercise 1 — Methods of a task. Write an object webTask with the data of task 3 from the backlog (id 3, "Update the bookings website", Lucía, high, pending, 14 h, '2026-10-02') and add three shorthand methods: isHighPriority() (returns true if the priority is 'high'), isOverdue(today) (applies R10) and label() (returns a string '[3] Update the bookings website — Lucía'). Try them with TODAY = '2026-09-20'.

Exercise 2 — Hunt the lost this. This code fails. Explain why and fix it in two different ways (one with bind and one with an arrow function), without changing the remind method.

'use strict';

const agenda = {
  assignee: 'Iván',
  remind(title) {
    console.log(`${this.assignee} must: ${title}`);
  }
};

const pending = ['Redesign the multipurpose room', 'Carpentry workshop quote'];
pending.forEach(agenda.remind);

Exercise 3 — Hours counter per person. Extend the board object with a method hoursFor(person) that returns the open hours (status other than 'done') for that person, and a method reportByPerson() that returns an object with one key per assignee and their open hours. Use this and have the methods call each other. Check that board.hoursFor('Iván') gives 25 and that the report adds up to the 45 total open hours.

Solutions

Exercise 1

'use strict';

const TODAY = '2026-09-20';

const webTask = {
  id: 3,
  title: 'Update the bookings website',
  assignee: 'Lucía',
  priority: 'high',
  status: 'pending',
  estimatedHours: 14,
  dueDate: '2026-10-02',

  isHighPriority() {
    return this.priority === 'high';
  },

  isOverdue(today) {
    return this.dueDate < today && this.status !== 'done';
  },

  label() {
    return `[${this.id}] ${this.title} — ${this.assignee}`;
  }
};

console.log(webTask.isHighPriority());   // true
console.log(webTask.isOverdue(TODAY));   // false (it is due on 2 October)
console.log(webTask.label());            // '[3] Update the bookings website — Lucía'

Notice that isOverdue receives today as a parameter instead of reading it from a global constant: it is the same design decision as in 03-03, and it lets you test the function with any date.

Exercise 2

The failure is in pending.forEach(agenda.remind). That does not pass "agenda's method", it passes the function stored in the remind property. When forEach invokes it, it does so with no receiver —a plain call— so in strict mode this is undefined and this.assignee throws a TypeError.

// Solution A: bind
pending.forEach(agenda.remind.bind(agenda));

// Solution B: wrapping arrow, which keeps the dot in the call
pending.forEach((title) => agenda.remind(title));

// Both print:
// Iván must: Redesign the multipurpose room
// Iván must: Carpentry workshop quote

A valid third option, specific to forEach, is to take advantage of its second parameter: pending.forEach(agenda.remind, agenda);.

Exercise 3

const board = {
  name: 'Taller Nómada',
  today: TODAY,
  tasks: [ /* the six backlog tasks */ ],

  open() {
    const result = [];
    for (const task of this.tasks) {
      if (task.status !== 'done') result.push(task);
    }
    return result;
  },

  hoursFor(person) {
    let total = 0;
    for (const task of this.open()) {          // reuses the previous method
      if (task.assignee === person) total += task.estimatedHours;
    }
    return total;
  },

  reportByPerson() {
    const report = {};
    for (const task of this.open()) {
      const key = task.assignee;
      report[key] = (report[key] ?? 0) + task.estimatedHours;
    }
    return report;
  }
};

console.log(board.hoursFor('Iván'));     // 25
console.log(board.hoursFor('Marta'));    // 6
console.log(board.hoursFor('Lucía'));    // 14
console.log(board.reportByPerson());     // { Iván: 25, Marta: 6, Lucía: 14 }

Check: 25 + 6 + 14 = 45 open hours, exactly those of the canonical backlog (the 3 h of "Screen-printing ink inventory" do not count because it is done). Two details of the code: report[key] uses brackets because the key is in a variable (04-01), and (report[key] ?? 0) handles the first time round, when the key does not exist yet and would return undefined. In 04-05 you will do this very same grouping with reduce in four lines.

Conclusion

A method is nothing more than a function stored in a property, and it is written with the shorthand syntax method() {}. What makes it special is this, and now you have the only rule you need: this depends on how the function is called, not on where it was written. With the four call forms in mind —plain call (undefined in strict mode), as a method (the receiver), with new (the new object, which you will develop in Module 5) and with call/apply/bind (whichever you say)— any mysterious this stops being mysterious: you just have to look at the call.

You have built the Taller Nómada board object, with findById, add, changeStatus, summary and line, confirming that methods call each other through this, that returning this allows operations to be chained and that errors are thrown with the throw new Error from 02-05. You have diagnosed the classic problem —losing this when passing a method as a callback— and you have the three solutions with their advantages and drawbacks. And you have settled the debt from 03-02: an arrow function has no this of its own, it inherits it from the scope where it was written, which makes it useless as an object-literal method and perfect as a callback inside a method.

Now think about what was most uncomfortable in this lesson. It was not this: it was the loops. findById walks by hand until it finds something; summary accumulates four variables in a for; hoursFor filters with an if inside another loop. The object is well modeled now, but the list of tasks is still being handled with the tools of Module 2. That changes from the next lesson onwards, Arrays: Basics and Methods, where you will study the array in depth: how it is created, how it is measured, and above all the distinction that saves the most grief in JavaScript —methods that mutate the array versus methods that return a new one— with a task's tags as the test bench.

JavaScript Course: From Beginner to Advanced

Module 1: Introduction to JavaScript

Module 2: Control Structures

Module 3: Functions

Module 4: Objects and Arrays

Module 5: Advanced Objects and Functions

Module 6: The Document Object Model (DOM)

Module 7: Browser APIs and Advanced Topics

Module 8: Testing and Debugging

Module 9: Performance and Optimization

Module 10: JavaScript Frameworks and Libraries

Module 11: Final Project

© Copyright 2026. All rights reserved