How this works
Inside a normal function, this is not fixed where the function is written: it is set every time the function is called, by the way it is called. Follow one function, hi, shared by two objects and called five ways: as cat.hi() and dog.hi(), with call, through bind, and as a plain f() that ends in a TypeError.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01In this.js, cat.hi and dog.hi are the very same function. Why does cat.hi() return "I am Tom" but dog.hi() return "I am Rex"?
Nothing is copied: cat.hi === dog.hi. In a method call obj.fn(), this is obj, so the same code reads a different name.
02In strict mode, what happens when you run const f = cat.hi; f(); ?
f does not remember cat. A plain call sets no this, so in strict mode this is undefined and reading this.name throws a TypeError.
03What does f.call(dog) do?
call runs the function straight away and uses its first argument as this. It returns "I am Rex".
04g = f.bind(cat). What does g.call(dog) return?
bind returns a new function whose this is locked. call, apply and a later bind cannot change it.
05Inside a method, you write a callback as an arrow function: () => this.name. Which this does the arrow use?
Arrow functions take this from the code around them, and call or bind cannot change it. That is why arrows suit callbacks inside methods.
Glossary
- this
- A hidden value every normal function receives each time it is called. The way the function is called decides what it is.
- Method call
- Calling a function through an object, as in cat.hi(). The object before the dot becomes this.
- Strict mode
- A stricter version of JavaScript, turned on by "use strict" and always on in classes and modules. A plain call there gets this = undefined.
- call
- A method every function has: fn.call(x, ...args) runs fn once with this set to x. apply does the same with the arguments in an array.
- bind
- A method every function has: fn.bind(x) returns a new function whose this is locked to x for good. It does not run fn.
- Arrow function
- A short function written () => ... . It has no this of its own and uses the this of the code around it.
- TypeError
- The error JavaScript throws when a value is used in a way its type does not allow, such as reading a property of undefined.
Notes and sources
- Without "use strict", a plain call like f() sets this to the global object (globalThis, which is window in a browser), so f() would not throw: it would read the global object's name instead. Code inside a class or a module (<script type="module">) is always strict.
- Arrow functions, such as () => this.name, have no this of their own. They see the this of the code around them, and call, apply and bind cannot change it. That is why arrows are handy for callbacks inside methods.
- With new, as in new Cat(), JavaScript makes a fresh object and calls the function with this set to that object. Class constructors work the same way.
- A bound function keeps its this for good: g.call(dog) still returns "I am Tom". apply is like call, but takes the other arguments as an array.
- The exact error text depends on the engine. Chrome and Node.js print "TypeError: Cannot read properties of undefined (reading 'name')"; Firefox and Safari word it differently. It is a TypeError in all of them.
- The boxes are a simplification: cat, dog, f and g are bindings that refer to objects, and the this box stands for the this binding that each call gets. hi exists once; cat.hi, dog.hi and f all refer to it.
Quiz Unlocks after the last step.