JavaScript 40 🧬 The this keyword — call/apply/bind
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
user.greet();
function sayHi() {
console.log('Hi, ' + this.name);
}
const bob = { name: 'Bob' };
sayHi.call(bob);
const alice = { name: 'Alice' };
sayHi.apply(alice);
const boundSayHi = sayHi.bind(bob);
boundSayHi();
const arrow = () => {
console.log('Arrow this:', this);
};
arrow();
The this keyword is one of the most confusing parts of JavaScript — until you understand one simple rule: this is determined by how a function is called, not where it’s defined. Once you internalize that rule, everything else falls into place.
Key point: this refers to the execution context of a function — the object that the function is invoked on. Different call styles (obj.method(), func(), new Func(), func.call(ctx)) set this differently. Arrow functions are the exception: they inherit this from their enclosing scope.
a – What is this and how it’s determined
this is a special keyword inside every function. Its value depends on how the function was called at runtime.
The one rule to remember:
this= the object the function is called on.
The four ways this is set:
| Call style | this value |
|---|---|
Method call — obj.fn() | obj |
Plain call — fn() | undefined (strict) / globalThis (sloppy) |
Constructor call — new Fn() | The newly created object |
Explicit — fn.call(ctx), fn.apply(ctx), fn.bind(ctx)() | ctx |
Method call — this is the object:
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
user.greet();
// [ Hello, Alice ]
this is user because the function is called on user.
Plain call — this is undefined (in strict mode):
'use strict';
function sayHi() {
console.log(this);
}
sayHi();
// [ undefined ]
In sloppy (non-strict) mode, this would be globalThis:
function sayHi() {
console.log(this === globalThis);
}
sayHi();
// [ true ] (in Node.js: global, in browsers: window)
Always use 'use strict' or ES modules so you get undefined instead of the global object. That way, mistakes like this.name on an unbound function throw instead of silently polluting globals.
Constructor call — this is the new object:
function Person(name) {
this.name = name;
}
const p = new Person('Alice');
console.log(p.name);
// [ Alice ]
new Person('Alice') creates a fresh object, sets this to it, and returns it.
Class methods behave the same:
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
const c = new Counter();
c.increment();
console.log(c.count);
// [ 1 ]
Extracting a method loses this:
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
const g = user.greet;
g();
// [ Hello, undefined ] ← this is not user anymore
The call is what matters — not the property. g() is a plain call, so this is undefined (or globalThis).
Why this matters:
- It lets methods access their own object’s data
- It enables sharing of one function across many objects
- It’s essential for callbacks, event handlers, and library design
- Understanding it prevents the classic “
thisis undefined” bug
b – call, apply, and bind
You can explicitly control what this is with call, apply, and bind. These are methods on every function.
call — invoke with a given this and arguments:
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const bob = { name: 'Bob' };
greet.call(bob, 'Hello', '!');
// [ Hello, Bob! ]
Signature: fn.call(thisArg, arg1, arg2, ...)
apply — same as call, but arguments as an array:
greet.apply(bob, ['Hi', '?']);
// [ Hi, Bob? ]
Signature: fn.apply(thisArg, [args])
call vs apply — the only difference:
| Method | Argument style |
|---|---|
call | Individual arguments |
apply | Array of arguments |
Before ES6 spread, apply was the way to pass a dynamic argument list. Today, fn(...args) is more idiomatic — but apply is still common in older code.
bind — return a new function with this fixed:
const boundGreet = greet.bind(bob);
boundGreet('Hey', '.');
// [ Hey, Bob. ]
bind doesn’t call the function — it returns a new function with this permanently set. You can also partially apply arguments:
const greetBob = greet.bind(bob, 'Hello');
greetBob('!');
// [ Hello, Bob! ]
The three compared:
function describe(age, city) {
console.log(`${this.name}, ${age}, ${city}`);
}
const alice = { name: 'Alice' };
describe.call(alice, 30, 'Paris');
// [ Alice, 30, Paris ]
describe.apply(alice, [30, 'Paris']);
// [ Alice, 30, Paris ]
const bound = describe.bind(alice, 30);
bound('Paris');
// [ Alice, 30, Paris ]
A table of differences:
| Method | Calls function? | Arguments | Returns |
|---|---|---|---|
call | Yes | Individual | Return value |
apply | Yes | Array | Return value |
bind | No | Individual (partial) | New function |
Using bind for event handlers:
class Button {
constructor(label) {
this.label = label;
}
click() {
console.log('Clicked: ' + this.label);
}
}
const btn = new Button('Save');
const handler = btn.click.bind(btn);
handler();
// [ Clicked: Save ]
Without bind, this would be lost when the handler is called by the event system.
Borrowing methods from other objects:
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const realArray = Array.prototype.slice.call(arrayLike);
console.log(realArray);
// [ [ 'a', 'b', 'c' ] ]
This pattern was common before Array.from existed.
bind for partial application:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
console.log(double(5));
// [ 10 ]
console.log(triple(5));
// [ 15 ]
The null is the thisArg — irrelevant here because multiply doesn’t use this.
bind is permanent:
const bound = greet.bind(bob);
const rebound = bound.bind(alice); // no effect
rebound('Hey', '.');
// [ Hey, Bob. ] ← still Bob
You can’t re-bind a bound function.
call and apply with no this needed:
function sum(a, b) {
return a + b;
}
console.log(sum.call(null, 1, 2));
// [ 3 ]
console.log(sum.apply(null, [1, 2]));
// [ 3 ]
Pass null or undefined when the function doesn’t use this.
A practical example — logging with context:
const logger = {
prefix: '[LOG]',
log(msg) {
console.log(`${this.prefix} ${msg}`);
}
};
logger.log('started');
// [ [LOG] started ]
const detached = logger.log;
const bound = detached.bind(logger);
bound('still works');
// [ [LOG] still works ]
Real-world use — array methods on array-likes:
function argsToArray() {
return Array.prototype.slice.call(arguments);
}
function show() {
console.log(argsToArray.apply(null, arguments));
}
bind in callbacks:
class Timer {
constructor() {
this.seconds = 0;
}
tick() {
this.seconds++;
console.log(this.seconds);
}
start() {
this.interval = setInterval(this.tick.bind(this), 1000);
}
}
Without bind, this inside tick would be undefined (or the timer object) because setInterval calls the function with no context.
c – Arrow functions and this
Arrow functions are the exception to every rule above. They don’t have their own this — they inherit it from where they’re defined.
Regular function vs arrow function:
const obj = {
name: 'Alice',
regular() {
console.log(this.name);
},
arrow: () => {
console.log(this.name);
}
};
obj.regular();
// [ Alice ]
obj.arrow();
// [ undefined ] ← this is not obj
The arrow function inherits this from the enclosing lexical scope — in this case, the module or global scope, where this.name is undefined.
this is captured at definition time:
const obj = {
name: 'Alice',
greet() {
const inner = () => {
console.log(this.name);
};
inner();
}
};
obj.greet();
// [ Alice ]
The inner arrow inherits this from greet, which is obj.
The same with a regular function would fail:
const obj = {
name: 'Alice',
greet() {
function inner() {
console.log(this.name); // this is undefined
}
inner();
}
};
obj.greet();
// TypeError: Cannot read properties of undefined
Arrow functions and call/apply/bind:
These have no effect on arrow functions:
const arrow = () => {
console.log(this);
};
const obj = { name: 'Alice' };
arrow.call(obj);
// [ undefined ] ← still lexical this
You can’t override an arrow’s this.
When arrows shine — callbacks that need this:
class Counter {
constructor() {
this.count = 0;
}
start() {
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
}
The arrow captures this from start, which is the Counter instance. No bind needed.
Comparison table:
| Aspect | Regular function | Arrow function |
|---|---|---|
Own this? | Yes | No (lexical) |
call/apply/bind | Works | Ignored |
| Constructor | Works with new | ❌ TypeError |
arguments object | Available | Not available |
| Method syntax | ✅ Recommended | ❌ Avoid |
| Callbacks | Needs bind | Inherits naturally |
When to use arrow functions:
- Callbacks inside methods
- Array methods (
map,filter,reduce) - Anywhere you want the outer
this - Never for object methods or constructors
When NOT to use arrow functions:
- Object methods —
obj.arrow = () => {}breaksthis - Class methods — use regular methods
- Constructors — arrows can’t be called with
new - Event handlers that need
event.currentTargetviathis(useevent.targetinstead)
The visual:
┌──────────────────────────────────────────────┐
│ Regular function │
│ │
│ function fn() { │
│ this ← set by caller │
│ } │
│ │
│ obj.fn() → this = obj │
│ fn() → this = undefined │
│ new fn() → this = new object │
│ fn.call(x) → this = x │
│ fn.bind(x)() → this = x │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Arrow function │
│ │
│ const fn = () => { │
│ this ← captured at definition │
│ } │
│ │
│ obj.fn() → this = lexical │
│ fn() → this = lexical │
│ fn.call(x) → this = lexical │
│ fn.bind(x)() → this = lexical │
│ │
└──────────────────────────────────────────────┘
Complete Example Session
// ============================================
// PART 1: METHOD CALL
// ============================================
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
user.greet();
// [ Hello, Alice ]
// ============================================
// PART 2: PLAIN CALL
// ============================================
'use strict';
function sayHi() {
console.log(this);
}
sayHi();
// [ undefined ]
// ============================================
// PART 3: CONSTRUCTOR CALL
// ============================================
function Person(name) {
this.name = name;
}
const p = new Person('Alice');
console.log(p.name);
// [ Alice ]
// ============================================
// PART 4: DETACHED METHOD
// ============================================
const detached = user.greet;
detached();
// [ Hello, undefined ]
// ============================================
// PART 5: CALL
// ============================================
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const bob = { name: 'Bob' };
greet.call(bob, 'Hello', '!');
// [ Hello, Bob! ]
// ============================================
// PART 6: APPLY
// ============================================
greet.apply(bob, ['Hi', '?']);
// [ Hi, Bob? ]
// ============================================
// PART 7: BIND
// ============================================
const boundGreet = greet.bind(bob);
boundGreet('Hey', '.');
// [ Hey, Bob. ]
// ============================================
// PART 8: PARTIAL APPLICATION WITH BIND
// ============================================
const greetBob = greet.bind(bob, 'Hello');
greetBob('!');
// [ Hello, Bob! ]
// ============================================
// PART 9: BIND IS PERMANENT
// ============================================
const alice = { name: 'Alice' };
const rebound = boundGreet.bind(alice);
rebound('Hey', '.');
// [ Hey, Bob. ]
// ============================================
// PART 10: BORROWING METHODS
// ============================================
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const realArray = Array.prototype.slice.call(arrayLike);
console.log(realArray);
// [ [ 'a', 'b', 'c' ] ]
// ============================================
// PART 11: PARTIAL APPLICATION
// ============================================
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5));
// [ 10 ]
// ============================================
// PART 12: ARROW FUNCTIONS
// ============================================
const obj = {
name: 'Alice',
regular() {
console.log(this.name);
},
arrow: () => {
console.log(this.name);
}
};
obj.regular();
// [ Alice ]
obj.arrow();
// [ undefined ]
// ============================================
// PART 13: ARROW INSIDE METHOD
// ============================================
const obj2 = {
name: 'Alice',
greet() {
const inner = () => {
console.log(this.name);
};
inner();
}
};
obj2.greet();
// [ Alice ]
// ============================================
// PART 14: REGULAR FUNCTION INSIDE METHOD
// ============================================
const obj3 = {
name: 'Alice',
greet() {
function inner() {
console.log(this.name);
}
inner();
}
};
try {
obj3.greet();
} catch (err) {
console.log(err.message);
}
// [ Cannot read properties of undefined (reading 'name') ]
// ============================================
// PART 15: ARROWS IGNORE CALL/APPLY/BIND
// ============================================
const arrow = () => {
console.log(this);
};
const o = { name: 'Alice' };
arrow.call(o);
// [ undefined ]
// ============================================
// PART 16: BIND FOR EVENT HANDLERS
// ============================================
class Button {
constructor(label) {
this.label = label;
}
click() {
console.log('Clicked: ' + this.label);
}
}
const btn = new Button('Save');
const handler = btn.click.bind(btn);
handler();
// [ Clicked: Save ]
// ============================================
// PART 17: ARROW FOR SETINTERVAL
// ============================================
class Counter {
constructor() {
this.count = 0;
}
start() {
setTimeout(() => {
this.count++;
console.log(this.count);
}, 100);
}
}
new Counter().start();
// [ 1 ]
// ============================================
// PART 18: COMPARING CALL/APPLY/BIND
// ============================================
function describe(age, city) {
console.log(`${this.name}, ${age}, ${city}`);
}
const alice2 = { name: 'Alice' };
describe.call(alice2, 30, 'Paris');
// [ Alice, 30, Paris ]
describe.apply(alice2, [30, 'Paris']);
// [ Alice, 30, Paris ]
const bound = describe.bind(alice2, 30);
bound('Paris');
// [ Alice, 30, Paris ]
// ============================================
// PART 19: LOGGER WITH BOUND METHOD
// ============================================
const logger = {
prefix: '[LOG]',
log(msg) {
console.log(`${this.prefix} ${msg}`);
}
};
const detachedLog = logger.log;
try {
detachedLog('oops');
} catch (err) {
console.log(err.message);
}
// [ Cannot read properties of undefined (reading 'prefix') ]
const boundLog = detachedLog.bind(logger);
boundLog('fixed');
// [ [LOG] fixed ]
// ============================================
// PART 20: FULL SCRIPT
// ============================================
const user40 = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
user40.greet();
function sayHi40() {
console.log('Hi, ' + this.name);
}
const bob40 = { name: 'Bob' };
sayHi40.call(bob40);
const alice40 = { name: 'Alice' };
sayHi40.apply(alice40);
const boundSayHi = sayHi40.bind(bob40);
boundSayHi();
const arrow40 = () => {
console.log('Arrow this:', this);
};
arrow40();
Quick Reference
The Four this Rules
| Call style | this value |
|---|---|
obj.method() | obj |
fn() | undefined (strict) |
new Fn() | New object |
fn.call/apply/bind(ctx) | ctx |
call, apply, bind
| Method | Calls fn? | Args | Returns |
|---|---|---|---|
call | Yes | Individual | Return value |
apply | Yes | Array | Return value |
bind | No | Individual | New function |
Function this
| Type | Own this? | call/apply/bind |
|---|---|---|
| Regular function | Yes | Works |
| Arrow function | No (lexical) | Ignored |
| Method shorthand | Yes | Works |
| Class method | Yes | Works |
call/apply/bind Syntax
| Method | Signature | Example |
|---|---|---|
| call | fn.call(ctx, a, b) | greet.call(bob, 'Hi', '!') |
| apply | fn.apply(ctx, [a, b]) | greet.apply(bob, ['Hi', '!']) |
| bind | fn.bind(ctx, a) | greet.bind(bob, 'Hi') |
Common Patterns
| Pattern | Code |
|---|---|
| Fix method | obj.method.bind(obj) |
| Partial apply | fn.bind(null, x) |
| Borrow method | Array.prototype.slice.call(arrLike) |
| Arrow callback | arr.map(x => this.foo(x)) |
| Event handler | element.addEventListener('click', this.handler.bind(this)) |
Arrow vs Regular
| Aspect | Arrow | Regular |
|---|---|---|
this | Lexical | Dynamic |
call/apply | Ignored | Works |
new | ❌ | ✅ |
arguments | ❌ | ✅ |
| Method syntax | ❌ | ✅ |
| Callback | ✅ | ⚠️ needs bind |
Strict Mode
| Mode | Plain call fn() |
|---|---|
Strict ('use strict') | this = undefined |
| Sloppy | this = globalThis |
Best Practices
✅ Do This:
// Use arrow functions for callbacks
arr.map(x => this.foo(x)); // ✅
// Bind methods passed as callbacks
setInterval(this.tick.bind(this), 1000); // ✅
// Use call/apply for borrowing
Array.prototype.slice.call(arrayLike); // ✅
// Use bind for partial application
const double = multiply.bind(null, 2); // ✅
// Use 'use strict' or ES modules
'use strict'; // ✅
// Use regular functions for object methods
const obj = {
method() { return this.x; }
}; // ✅
// Use arrow for outer this
class Timer {
start() {
setTimeout(() => this.tick(), 1000);
}
} // ✅
// Pass null when this isn't needed
sum.call(null, 1, 2); // ✅
❌ Don’t Do This:
// Don't use arrow functions as methods
const obj = {
arrow: () => this.x // ❌ this is lexical
};
// Don't detach methods without binding
const g = user.greet;
g(); // ❌ this lost
// Don't rely on sloppy mode this
function fn() { return this; } // ❌ globalThis
fn();
// Don't forget bind in callbacks
setInterval(this.tick, 1000); // ❌ this lost
setInterval(() => this.tick(), 1000); // ✅
setInterval(this.tick.bind(this), 1000); // ✅
// Don't use call/apply on arrows
arrow.call(ctx); // ❌ ignored
// Don't use new with arrows
new (() => {})(); // ❌ TypeError
// Don't rebind a bound function
const bound = fn.bind(a);
const rebound = bound.bind(b); // ⚠️ no effect
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Detached method | this lost | bind |
| Arrow as method | this is lexical | Use regular method |
| Callback without bind | this undefined | Arrow or bind |
call on arrow | Ignored | Use regular function |
new with arrow | TypeError | Use regular function |
| Sloppy mode | this is global | 'use strict' |
| Rebinding bound | No effect | Bind once |
Assuming this is the function | Wrong model | this is the caller |
Real-World Examples
1. Method Call
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
user.greet();
// [ Hello, Alice ]
2. Detached Method Loses this
const g = user.greet;
g();
// [ Hello, undefined ]
3. call
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const bob = { name: 'Bob' };
greet.call(bob, 'Hello', '!');
// [ Hello, Bob! ]
4. apply
greet.apply(bob, ['Hi', '?']);
// [ Hi, Bob? ]
5. bind
const boundGreet = greet.bind(bob);
boundGreet('Hey', '.');
// [ Hey, Bob. ]
6. Partial Application with bind
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5));
// [ 10 ]
7. Borrow Array Method
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const arr = Array.prototype.slice.call(arrayLike);
console.log(arr);
// [ [ 'a', 'b', 'c' ] ]
8. Arrow Inside Method
const obj = {
name: 'Alice',
greet() {
const inner = () => console.log(this.name);
inner();
}
};
obj.greet();
// [ Alice ]
9. Regular Function Inside Method Fails
const obj = {
name: 'Alice',
greet() {
function inner() {
console.log(this.name);
}
inner();
}
};
try {
obj.greet();
} catch (err) {
console.log(err.message);
}
// [ Cannot read properties of undefined (reading 'name') ]
10. Arrow as Method Fails
const obj = {
name: 'Alice',
arrow: () => console.log(this.name)
};
obj.arrow();
// [ undefined ]
11. Arrows Ignore call/apply
const arrow = () => console.log(this);
const o = { name: 'Alice' };
arrow.call(o);
// [ undefined ]
12. Bind for Event Handler
class Button {
constructor(label) {
this.label = label;
}
click() {
console.log('Clicked: ' + this.label);
}
}
const btn = new Button('Save');
const handler = btn.click.bind(btn);
handler();
// [ Clicked: Save ]
13. Arrow for setInterval
class Counter {
constructor() {
this.count = 0;
}
start() {
setTimeout(() => {
this.count++;
console.log(this.count);
}, 100);
}
}
new Counter().start();
// [ 1 ]
14. Constructor this
function Person(name) {
this.name = name;
}
const p = new Person('Alice');
console.log(p.name);
// [ Alice ]
15. Class Method this
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
move(dx, dy) {
this.x += dx;
this.y += dy;
}
}
const pt = new Point(0, 0);
pt.move(1, 2);
console.log(pt.x, pt.y);
// [ 1 2 ]
16. Bind Once
function fn() { return this.x; }
const a = { x: 1 };
const b = { x: 2 };
const boundA = fn.bind(a);
const rebound = boundA.bind(b);
console.log(rebound());
// [ 1 ] ← still bound to a
17. Chaining with Method Return this
class Builder {
constructor() {
this.parts = [];
}
add(part) {
this.parts.push(part);
return this;
}
build() {
return this.parts.join('-');
}
}
const result = new Builder().add('a').add('b').add('c').build();
console.log(result);
// [ a-b-c ]
18. Borrow Math Method
const numbers = [5, 3, 9, 1];
const max = Math.max.apply(null, numbers);
console.log(max);
// [ 9 ]
const max2 = Math.max(...numbers);
console.log(max2);
// [ 9 ]
19. Strict vs Sloppy this
function sloppyThis() {
return this;
}
function strictThis() {
'use strict';
return this;
}
console.log(typeof sloppyThis());
// [ object ]
console.log(typeof strictThis());
// [ undefined ]
20. Full Script
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
}
};
user.greet();
function sayHi() {
console.log('Hi, ' + this.name);
}
const bob = { name: 'Bob' };
sayHi.call(bob);
const alice = { name: 'Alice' };
sayHi.apply(alice);
const boundSayHi = sayHi.bind(bob);
boundSayHi();
const arrow = () => {
console.log('Arrow this:', this);
};
arrow();
Visual: this Determination
┌──────────────────────────────────────────────┐
│ How was the function called? │
│ │
│ obj.method() ──► this = obj │
│ │
│ fn() ──► this = undefined (strict) │
│ this = globalThis (sloppy)│
│ │
│ new Fn() ──► this = new object │
│ │
│ fn.call(x) ──► this = x │
│ fn.apply(x) ──► this = x │
│ fn.bind(x)() ──► this = x │
│ │
│ Arrow function: this = enclosing scope │
│ │
└──────────────────────────────────────────────┘
Visual: call vs apply vs bind
┌──────────────────────────────────────────────┐
│ fn.call(ctx, a, b) │
│ │ │
│ └──► calls fn immediately │
│ with this = ctx, args a, b │
│ returns fn's return value │
│ │
│ fn.apply(ctx, [a, b]) │
│ │ │
│ └──► same as call, but args as array │
│ │
│ fn.bind(ctx, a) │
│ │ │
│ └──► returns new function │
│ this = ctx, a pre-applied │
│ call the new fn to run it │
│ │
└──────────────────────────────────────────────┘
Visual: Arrow vs Regular this
┌──────────────────────────────────────────────┐
│ Regular function: │
│ │
│ obj = { │
│ name: 'Alice', │
│ greet() { ← function │
│ // this = obj when called as obj.greet()│
│ } │
│ } │
│ │
│ obj.greet() → this = obj ✅ │
│ greet() → this = undefined ❌ │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Arrow function: │
│ │
│ obj = { │
│ name: 'Alice', │
│ arrow: () => { ← arrow │
│ // this = whatever was outside obj │
│ } │
│ } │
│ │
│ obj.arrow() → this ≠ obj ❌ │
│ │
│ Use regular functions for methods! │
│ │
└──────────────────────────────────────────────┘
Summary
| Concept | Syntax | Example |
|---|---|---|
| Method this | obj.method() | this = obj |
| Plain this | fn() | this = undefined |
| Constructor this | new Fn() | this = new object |
| call | fn.call(ctx, a, b) | Invoke with ctx |
| apply | fn.apply(ctx, [a, b]) | Same, array args |
| bind | fn.bind(ctx) | Return new fn |
| Partial apply | fn.bind(null, x) | Pre-fill args |
| Borrow | Array.prototype.slice.call(x) | Use method of another type |
| Arrow this | () => this | Lexical |
| Arrow + call | arrow.call(x) | Ignored |
| Strict mode | 'use strict' | this = undefined |
Key takeaways:
thisdepends on how a function is called, not where it’s definedobj.method()→thisisobjfn()→thisisundefinedin strict mode,globalThisin sloppynew Fn()→thisis the new objectcall,apply,bindlet you setthisexplicitlycallandapplyinvoke immediately;bindreturns a new functioncalltakes individual args,applytakes an array- Arrow functions inherit
thisfrom the enclosing scope and ignorecall/apply/bind - Use regular functions for object methods and constructors
- Use arrow functions for callbacks where you want the outer
this - Use
bindwhen you need to pass a method as a callback and keepthis - Partial application via
bindis a powerful pattern for reusable functions
Remember: this is set by the call, not the definition. Method call → the object. Plain call → undefined. Constructor → the new object. call/apply/bind → whatever you pass. Arrow functions are the exception — they don’t have their own this. Use arrows for callbacks, regular functions for methods. When in doubt, log this and trace how the function was invoked. Master this, and the most confusing part of JavaScript becomes the most predictable.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!