Step 1 of 7 · Reading · ~4 min
Learn
Objects in Depth
this and Methods
Inside a function, this is an extra hidden argument, and which value it gets depends on how the function is called, not where it was written. By the end of this lesson you can name the four ways this gets its value, say why arrow functions are the exception, and fix the "lost this" bug.
The surprise
Both calls run the same function. const detached = account.summary copies a reference to the function, not a link to account. Called bare, nothing is to the left of the dot, so this is no longer the account. In a plain script this falls back to the global object, which has no holder, giving undefined. In strict mode (and always inside an ES module) it is undefined itself, and reading this.holder throws a TypeError.
The four rules
When a regular function is called, look at the call site:
- Method call,
obj.fn():thisis the object left of the dot (accountabove). - Plain call,
fn():thisis the global object (sloppy mode) orundefined(strict mode). newcall,new Fn():thisis a fresh empty object thatnewcreates and returns. Forgettingnewis rule 2 in disguise: the assignments land on the global object.- Explicit, via
call,applyorbind:thisis whatever you pass.
bind also presets leading arguments: const add10 = ((a, b) => a + b).bind(null, 10); makes add10(5) return 15.
Arrow functions have no this of their own
An arrow function does not take part in the four rules. Its this is whatever this was where the arrow was written, like any other captured variable. That makes arrows ideal for callbacks inside methods:
report is called as a method, so this is timer, and the arrow inherits it. In broken the callback is a regular function called plainly by map, so its this is the global object. The flip side: an arrow is the wrong choice for an object's own method, because { greet: () => this.name } takes this from the surrounding scope, not from the object.
Losing this, and the three fixes
Passing a method somewhere that will call it later (setTimeout, an event handler, array.map) detaches it, exactly like detached above. Strict mode makes it loud:
Fixes: bind at the hand-off (job.run.bind(job)), wrap in an arrow (() => job.run()), or avoid this altogether by keeping the data in a closure, as in the closures lesson.
Common mistakes
- Extracting a method and calling it bare:
const { run } = job;. - Using
functionfor a callback inside a method and expecting the outerthis. - Using an arrow function as an object method that needs
this. - Forgetting
new, which silently writes properties onto the global object. - Trying to re-bind a bound function with
call. It has no effect. - Writing
fn().bind(x): that callsfnfirst. The.bindgoes on the function itself.
Your exercise
You have just seen what bind returns; now write one. The usual slip is using the this of the wrapper you return instead of the thisArg you were given.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…