Methods & this
Learn how functions stored on objects become methods, how this is chosen by the call shape, and how returning this powers method chaining.
- 01Write method shorthandStore a function on an object with
greet() {}, and read other properties of that object withthis. - 02Predict thisFind the object before the dot to say what
thisis inobj.method(), and spot a call that losesthis. - 03Build chainsReturn
thisfrom methods that change the object, so several calls can follow one another in one line.
Methods are functions that belong to objects
In Object basics, objects held data, such as names, scores, and settings. Each piece of data sits in a property: a name with a value. A property can also hold a function. A function stored in an object property is called a method.
const dog = { name: "Pixel", bark: function () { return this.name + " says woof"; },}; console.log(dog.bark()); // Pixel says woofLine 3 stores a function in the property bark. Line 4 uses this.name. It reads the name of the object that called the method. Line 8 calls the method through dog. So this is dog, and the program prints Pixel says woof.
Why not write dog.name on line 4? Because one function can be shared by many objects. dog.name would always read the dog's name, even when another object calls the function. The special word this means "the object this method is working on right now".
function greet() { return "Hi " + this.name;} const asha = { name: "Asha", greet };const rae = { name: "Rae", greet }; console.log(asha.greet());console.log(rae.greet());Line 1 makes one plain function, greet. Line 2 doesn't name Asha or Rae. Lines 5 and 6 store that one function on two objects. Writing greet alone inside the braces is short for greet: greet.
Line 8 calls the function through asha, so it prints Hi Asha. Line 9 calls it through rae, so it prints Hi Rae.
greet function. The object before the dot becomes this, so the same function greets Asha in one call and Rae in the other.const cart = { items: 0, addTea() { this.items = this.items + 1; return this.items; },}; console.log(cart.addTea());Line 4 adds 1 to items on the cart that made the call. Line 9 calls the method through cart, so it prints 1. This is a common job for a method. The object keeps its data and the action that changes that data in one place.
Think of an object as a character card in a game. The card lists facts, like a name. It also lists skills, like bark. A method is one of those skills. When Pixel uses a skill, the word "my" means Pixel.
- In real life: Pixel has the skill bark
- In JavaScript:
dog.barkholds a function - In real life: The skill can use Pixel’s name
- In JavaScript: The method reads
this.name - In real life: Another dog can have the same skill
- In JavaScript: The same function can be called as
nori.bark()
This lesson teaches one main rule. When you call a method as object.method(), this is the object before the dot. JavaScript chooses it at the moment of the call.
There are more rules for this, and Stage 4 has a full module on them. It covers call, apply, and bind, which set this by hand, plus what this is outside any object and more strict-mode details.
Method shorthand
STEP THROUGHMost methods are written in a shorter way, called method shorthand. Instead of bark: function () { ... }, you write bark() { ... }. For everyday calls, both work the same. Both store a regular function in the bark property.
There is one small difference for later. You can't use a shorthand method with new. You'll meet new in the next lesson, Constructors & new.
const oldStyle = { name: "Ada", greet: function () { return "Hi " + this.name; },}; const shorthand = { name: "Ada", greet() { return "Hi " + this.name; },};Line 3 is the old style. Line 10 is the shorthand. The shorter spelling is not the important part. The important part is how the method reads data from its object. In both, this.name means "the name property of the object using this method right now".
const user = { name: "Asha", greet() { return "Hi " + this.name; },}; console.log(user.greet());Line 3 makes a method named greet. Line 4 reads the name of the object that called it. Line 8 calls it through user, so it prints Hi Asha. Use a shorthand method when an action needs other properties of the same object.
- A method can return a value, change its object, or do both.
- Use a regular method when it reads
this.name,this.total, or another property of the same object. - If a helper needs no data from an object, a plain function is usually clearer.
Predict what dog.bark() prints. Then step through to see how method shorthand and this work together.
script
name: "Pixel", bark() { return this.name + " says woof"; },}; console.log(dog.bark());A method is a function value stored in a property. The function doesn't stay tied to that object forever. That's why the next section looks at how the function is called, not where it was written.
this is decided at call time
INTERACTIVEHere is the rule to practice: look at the call itself. In ada.introduce(), the object before the dot is ada, so this is ada. In lin.introduce(), the object before the dot is lin. So the same function gets a different this.
Choose the last line, then step through to see the same function get a different this.
script
"use strict"; return "Hi, I am " + this.name;} const ada = { name: "Ada", introduce };const lin = { name: "Lin", introduce }; ada.introduce();Now try the third choice, "Saved copy". Line 9 saves the function in a new variable, speak. Line 10 calls speak(). That call has no dot, so there is no object before a dot.
The code starts with "use strict". That line turns on strict mode, a stricter set of JavaScript rules. Code in JavaScript modules, like the code on this site, always runs in strict mode. In strict mode, a call with no object gives this the value undefined. Reading this.name then throws a TypeError: the error JavaScript throws when you use a value in a way it can't be used.
ada.introduce(), it works on Ada. Saved in its own variable and called as speak(), it has no object to work on.The word "my" changes meaning with the speaker. If Ada says "my laptop", it is Ada's laptop. If Lin says the same words, it is Lin's laptop. this works the same way in a method call. Its meaning comes from the object that makes the call right now.
- In real life: Ada says “my name”
- In JavaScript:
ada.introduce()makesthisbeada - In real life: Lin says “my name”
- In JavaScript:
lin.introduce()makesthisbelin - In real life: A note says “my name” but nobody signed it
- In JavaScript:
speak()has no object before the dot
const app = { user: { name: "Lin", introduce() { return "Hi " + this.name; }, },}; console.log(app.user.introduce());The call on line 10 is app.user.introduce(). It has two dots. The object right before introduce is app.user, not the outer app. So this is app.user, and line 10 prints Hi Lin.
Arrow functions, callbacks, and losing this
SORTYou met arrow functions, like () => 1, in the Arrow functions lesson. An arrow function has no this of its own. Inside an arrow, this means whatever this meant in the code around it. That helps in one place and hurts in another:
- Helpful: an arrow inside a method keeps the method's
this. - Not helpful: an arrow used as the method doesn't get the object as
thiswhen you callobject.method().
The table below compares four ways to put a function on an object. Read each row as: "if I write this, is this the object, and when should I use it?"
| When you write | Is this the object? | When to use it |
|---|---|---|
greet() { return this.name; } | Yes, when you call obj.greet(). | For a method that reads or changes other properties of the same object. |
greet: function () { return this.name; } | Yes, when you call obj.greet(). | This is the older way to write the same method. |
greet: () => this.name | No. The arrow keeps the this from outside the object. | Almost never. Don't use an arrow as a method that needs this. |
() => obj.greet() | The arrow has no this of its own. But it runs the real call obj.greet(), so greet gets obj. | As a wrapper when you pass a method to other code. |
const user = { name: "Mina", regular() { return this.name; }, arrow: () => this.name,}; console.log(user.regular());console.log(user.arrow());Line 3 is a regular method, so line 9 calls it with user as this and prints Mina. Line 6 is an arrow. It doesn't get user as this. It uses the this from the code outside the object, which has no name. In this example, line 10 prints undefined.
const team = { prefix: "Team", names: ["Ada", "Lin"], labels() { return this.names.map((name) => this.prefix + ": " + name); },}; console.log(team.labels().join(", "));Line 4 makes a regular method, labels(). Line 5 uses map. map runs a function once for each item in an array and collects the results in a new array.
The function given to map is an arrow. The arrow has no this of its own, so it uses the this of labels(), which is team. Line 9 joins the results with join(", ") and prints Team: Ada, Team: Lin. The arrow is inside the method; it is not the method itself.
Callbacks are the other common way to lose this. A callback is a function you give to other code, so that code can call it later. For example, setTimeout(fn, 1000) calls fn after 1000 milliseconds, which is one second.
If you pass timer.announce to setTimeout, you pass only the function. Later, setTimeout calls that function on its own, not as timer.announce(). So the function no longer gets timer as this. A simple fix is an arrow wrapper: () => timer.announce(). In Stage 4 you'll also learn bind, another fix.
const timer = { label: "tea is ready", announce() { return this.label; },}; const later = () => timer.announce();console.log(later());Line 8 makes a new function, later. It doesn't run announce yet. When line 9 calls later(), the arrow runs timer.announce(). That call has timer before the dot, so this is timer, and line 9 prints tea is ready. Use a wrapper like this for clicks, timers, and other callbacks that run a method later.
timer.announce hands over only the function. The timer is left behind. The wrapper () => timer.announce() keeps the whole call, dot included, and runs it later.dog.bark()app.user.sayName()const fn = dog.bark; fn()setTimeout(dog.bark, 0)setTimeout(() => dog.bark(), 0)const dog = { bark: () => this.name }
Sort each call by what happens to this. Look at the object before the last dot.
Method chaining
INTERACTIVEMethod chaining means calling several methods in one line, one after another, like counter.add(2).add(3).double().read().
It works because each method that changes the object ends with return this. What it returns is the same counter. So the next dot has an object to call a method on.
add(2) and add(3) return this, the same counter, so the next dot has an object to call. read() returns the number 5, so the chain ends there.In a factory, an assembly line is a row of work stations. Each station works on the same product, then passes it to the next station. If one station forgets to pass it on, the next station has nothing to work on. A chained method that forgets return this causes the same problem.
- In real life: Station 1 adds a part and passes the product on
- In JavaScript:
add(2)changes the object and returnsthis - In real life: Station 2 works on the same product
- In JavaScript:
.add(3)gets the same object - In real life: The last station reports the result
- In JavaScript:
.read()returns the final value
Step through a chain. Watch each method that changes the counter return this, so the next method has an object to call.
script
value: 0, add(amount) { this.value = this.value + amount; return this; }, double() { this.value = this.value * 2; return this; }, read() { return this.value; },}; counter.add(2).add(3).double().read();const cart = { total: 0, add(price) { this.total = this.total + price; return this; },}; const sameCart = cart.add(3);console.log(sameCart === cart);console.log(cart.total);Line 5 returns the cart itself, not a copy. Line 9 saves what cart.add(3) returned in sameCart. Line 10 uses === to check whether sameCart and cart are the very same object. It prints true.
The method also changed the cart's total, so line 11 prints 3. So a chain usually changes the original object.
const counter = { value: 1, add() { this.value = this.value + 1; return this; }, read() { return this.value; },}; console.log(counter.add().read());Line 5 keeps the chain going: it returns the counter. Line 8 is different: read() returns a number, not the counter. The value starts at 1, and one add() makes it 2. So line 12 prints 2. End a chain with a method like this when you need the final value, for example to show it on the page.
const counter = { value: 0, add(amount) { this.value = this.value + amount; return this; }, double() { this.value = this.value * 2; return this; }, read() { return this.value; },}; counter.add(2).add(3).double().read();counter
The error you get when a method forgets to return this: TypeError: Cannot read properties of undefined (reading 'double')
Build a chain one method at a time. Start with add(2), then try double(), then break the chain with brokenAdd(2).
Chain only when each call continues the same simple task. Don't pack complicated decisions into one long line. A few clear lines are better than one clever chain.
Where you’ll use this
You'll use methods wherever an object has both data and actions. For example:
- a form checker that tests what a user typed
- a shopping cart that adds items and adds up the total
- a counter, or a tool that formats dates
- a small object that remembers the state of one part of a page
The object keeps related values together. Its methods are the actions that make sense for those values.
const headline = { text: "", word(value) { this.text = this.text + value + " "; return this; }, shout() { this.text = this.text.toUpperCase(); return this; }, read() { return this.text.trim(); },}; console.log(headline.word("methods").word("click").shout().read());Line 16 reads like a sentence: add a word, add another word, shout, then read. On line 8, toUpperCase() turns the text into capital letters. On line 12, trim() removes the extra space at the end. The program prints METHODS CLICK.
It works because of the same small rule. Each method that changes the text uses this for the current object. Then it returns this for the next call.
- Use a method when an action belongs with one object's data, such as
cart.add(price). - Let a read method such as
read()only return a value. It shouldn't also change the object. - To update a page, call one named method, such as
profile.rename(name). Don't change the same properties from many places in your code. - Don't make a method just to hold a calculation that doesn't use the object. A plain function is easier to test and reuse.
Common misconceptions
“A method always knows its object.”
No. You can copy a function into another variable or object. this is chosen by how you call the function, not by where it was first stored.
“this means the object where the function was written.”
No. The same function can be stored on two objects. ada.introduce() gets ada as this, and lin.introduce() gets lin.
“Arrow functions are just shorter methods.”
They are shorter, but they have no this of their own. Use a regular method when it reads or changes the object's properties.
“Callbacks keep their object.”
No. Passing user.say on its own passes only the function. Wrap it as () => user.say(), or use bind, which you'll learn in Stage 4.
“Every method should return this.”
Only methods meant to continue a chain should return this. A method such as read() should return the value you need.
Constructors use this too, but with a different kind of call: new Something(). That's the next lesson, Constructors & new.
Practice: methods and this
6 EXERCISESAdd a describe method to the book object. It should print the exact sentence the answer box expects.
const book = {
title: "Eloquent JavaScript",
describe() {
return this.title + " is on the shelf";
},
};
console.log(book.describe());const book = {
title: "Eloquent JavaScript",
describe() {
return this.title + " is on the shelf";
},
};
console.log(book.describe());describe() is a method, because it is a function stored on book. You call it as book.describe(), so this is book. Then this.title is the book's title.
Predict both printed lines before you run the code.
"use strict";
function label() {
return this.name;
}
const first = { name: "first", label };
const second = { name: "second", label };
console.log(first.label());
console.log(second.label());first.label() makes this be first, so the first line is first. second.label() makes this be second, so the second line is second.
Imagine some other code will call callback() later. Make the callback print break time.
const timer = {
label: "break time",
announce() {
console.log(this.label);
},
};
const callback = () => timer.announce();
callback();const timer = {
label: "break time",
announce() {
console.log(this.label);
},
};
const callback = () => timer.announce();
callback();The arrow wrapper has no this of its own, but it doesn't need one. Its body runs timer.announce(). That call has timer before the dot, so announce gets timer as this.
Build a small object whose word method can be called twice in one chain.
const list = {
text: "",
word(value) {
this.text = this.text + value;
return this;
},
read() {
return this.text;
},
};
console.log(list.word("go").word("!").read());const list = {
text: "",
word(value) {
this.text = this.text + value;
return this;
},
read() {
return this.text;
},
};
console.log(list.word("go").word("!").read());The first word call returns the same list object, so the second .word("!") has an object to call. read() returns the final string, which ends the chain.
A checkout page adds a tea for 3 and a snack for 2. Finish the cart methods so one chain prints 5. Many small cart, filter, and builder tools work this way.
const cart = {
total: 0,
add(price) {
this.total = this.total + price;
return this;
},
readTotal() {
return this.total;
},
};
console.log(cart.add(3).add(2).readTotal());const cart = {
total: 0,
add(price) {
this.total = this.total + price;
return this;
},
readTotal() {
return this.total;
},
};
console.log(cart.add(3).add(2).readTotal());add changes the cart's own total and returns the cart for the next item. readTotal() ends the chain. It returns the number, so a checkout page can show it.
Rewrite the greeting as a regular method, so this.name reads the object's name.
const user = {
name: "Rae",
greet() {
return "Hi " + this.name;
},
};
console.log(user.greet());const user = {
name: "Rae",
greet() {
return "Hi " + this.name;
},
};
console.log(user.greet());A regular shorthand method gets user as this when you call user.greet(). The arrow version would use the this from outside the object instead.
Quiz: check your understanding
7 QUESTIONSEach choice comes with a short explanation. For code questions, find the object before the dot first.
Question 1 of 7Which object is
thisinsidecat.meow()?Choose an answer to see the explanation.
Question 2 of 7What does this pet method print?
Read the code, then predictconst pet = { name: "Nori", speak() { console.log(this.name); }, }; pet.speak();Choose an answer to see the explanation.
Question 3 of 7Which of these is method shorthand?
Choose an answer to see the explanation.
Question 4 of 7This strict-mode code saves a method in a variable, then calls it on its own. What happens?
Read the code, then predict"use strict"; const user = { name: "Ari", say() { console.log(this.name); }, }; const fn = user.say; fn();Choose an answer to see the explanation.
Question 5 of 7Why does
counter.add(2).double()work?Choose an answer to see the explanation.
Question 6 of 7What does this counter chain print?
Read the code, then predictconst counter = { value: 1, add() { this.value = this.value + 1; return this; }, read() { console.log(this.value); }, }; counter.add().add().read();Choose an answer to see the explanation.
Question 7 of 7Which callback still calls the method on
user?Choose an answer to see the explanation.
Key takeaways
- A method is a function stored in an object property.
- Method shorthand,
greet() { ... }, is the usual way to write a method. - In
obj.method(),thisisobj. JavaScript chooses it when the call happens. - A saved copy, or a callback passed on its own, loses the object before the dot. An arrow wrapper, or
bind, fixes that. - Methods that continue a chain return
this. The last method in a chain returns the value you need.
Remember the one-liner.
A method is a function stored on an object; this is the object before the dot in that call.
Up next: Constructors & new.