Chapter 13 of 28 · JavaScript for Beginners — Complete Free Tutorial
The this Keyword & Binding
this is a special binding that points to “the object the code is running for”. Unlike most languages, JavaScript decides what this means at call time, by how the function is invoked — not where it was written. Once you learn the four binding rules, every this surprise becomes predictable.
Why this exists
One method definition can serve many objects. this is the shortcut that says “the current object”, so user.getName() and admin.getName() can share code while returning different data.
const user = { name: 'Asha', greet() { return `Hi, ${this.name}`; } };
const admin = { name: 'Kai', greet: user.greet }; // same function…
console.log(user.greet()); // → Hi, Asha
console.log(admin.greet()); // → Hi, Kai (this = whoever called it)
Rule 1: implicit binding (method call)
const counter = {
count: 0,
increment() { this.count++; },
};
counter.increment(); // 'this' is counter (the object before the dot)
console.log(counter.count); // → 1
The rule: the object immediately to the LEFT of the dot when the function is called becomes this.
Rule 2: default binding
function show() {
console.log(this);
}
show(); // sloppy mode → window (browser global)
// strict mode / module → undefined ✅ (modules are strict!)
When a function is called plainly — no object before the dot — this falls back to the global object (or undefined in strict/module code, which is safer: accidental globals throw instead of appearing).
Rule 3: explicit binding (call, apply, bind)
function greet(greeting) {
return `${greeting}, ${this.name}`;
}
const asha = { name: 'Asha' };
console.log(greet.call(asha, 'Hello')); // 'this' forced → asha, args separate
console.log(greet.apply(asha, ['Hello'])); // same, args in an array
const bound = greet.bind(asha); // returns a NEW function with 'this' fixed
console.log(bound('Yo')); // → Yo, Asha
| Method | Calls now? | Arguments |
|---|---|---|
call | Yes, immediately | List: call(thisArg, a, b) |
apply | Yes, immediately | Array: apply(thisArg, [a, b]) |
bind | No — returns a wrapper | List, plus any later call's args |
bind is the one you will use most: locking this for callbacks.
Rule 4: new binding (constructors and classes)
class Task {
constructor(title) { this.title = title; } // 'this' = brand-new object
}
const t = new Task('Ship');
console.log(t.title); // → Ship
new creates an empty object, sets it as this, runs the constructor, and returns the object.
The resolution order (check in this sequence)
- new in the call? → the new object.
- call/apply/bind used? → the object they specified.
- Called as
obj.method()? →obj. - Otherwise → global object or
undefined(strict).
Arrow functions: this is frozen at creation
const timer = {
count: 0,
start() {
// regular function inside a method — 'this' re-binds on each call
setTimeout(function () {
console.log(this.count); // → undefined (called with no object)
}, 100);
// arrow — inherits 'this' from start(), which is the timer object
setTimeout(() => {
console.log(this.count); // → works ✅
}, 100);
},
};
Arrows have no own this — they close over the surrounding scope's this (lexical this, implemented via closures). This is precisely why arrows are the default for callbacks inside methods and event handlers.
this in event handlers
button.addEventListener('click', function () {
console.log(this); // the button (implicit binding by the DOM)
});
button.addEventListener('click', (e) => {
console.log(this); // outer scope's this — usually module/global
console.log(e.currentTarget); // ✅ the reliable way: use the event object
});
Inside listeners, prefer e.currentTarget (or e.target.closest(...)) over this — it works with arrows, nested handlers and delegation alike.
this in classes
class Timer {
seconds = 0;
start() {
this.id = setInterval(() => {
this.seconds++; // arrow keeps the instance as 'this'
}, 1000);
}
stop() { clearInterval(this.id); }
}
Methods get instance this automatically; callbacks inside them need arrows (or bind) to keep it. Getters/setters: this is the instance being accessed.
The classic mistakes
// 1. Detached method — 'this' lost when passed as a value
const counter = { count: 0, inc() { this.count++; } };
const fn = counter.inc;
fn(); // ❌ TypeError (strict: this is undefined)
counter.inc.bind(counter)(); // ✅ fixed with bind
counter.inc(); // ✅ or call it as a method
// 2. setTimeout without arrow (shown above) — this becomes window/undefined
// 3. that = this workaround — works, but arrows replace it entirely
const self = this;
// 4. Arrow as a class method field — possible, but it is a per-instance
// function, not a shared prototype method (different semantics)
Practical patterns
// partial application with bind
function product(price, tax) { return price * (1 + tax); }
const withGst = product.bind(null, 100, 0.18); // 'this' irrelevant → null
console.log(withGst()); // → 118
// method chaining via returning this
class Builder {
#parts = [];
add(p) { this.#parts.push(p); return this; } // return this → chainable
build() { return [...this.#parts]; }
}
console.log(new Builder().add('a').add('b').build()); // → ['a', 'b']
Common mistakes (recap)
- Assuming
thisis lexical — for regular functions it depends on how you call, not where you write. - Passing methods as callbacks unbound —
setTimeout(obj.method, 0)loses the object; wrap in an arrow orbind. thisin plain module code — top-levelthisin a module isundefined, notwindow.- Forcing
thiswith arrows — arrows cannot be re-bound;call/bindsilently do nothing to them.
Practice checklist
- Take a method, detach it (
const f = obj.m), call it, observe the failure — then fix withbind. - Build a chained
Builderusingreturn this. - Write a class with a timer callback; convert it from
functionto arrow and watchthiscome back. - Predict
thisin six calls (method, detached,call,bind, arrow,new) — then verify withconsole.log(this).
Context understood — time to use it on real pages. Next: The DOM: Selecting Elements.
Frequently Asked Questions
JavaScript is the programming language of the web. It adds behaviour to pages: responding to clicks and input, validating forms, fetching data from APIs, updating the DOM without reloading, and much more. It also runs on servers (Node.js), in desktop and mobile apps, and in build tooling.
They are unrelated languages that share a similar name for historical marketing reasons. Java is a statically-typed compiled language; JavaScript is a dynamically-typed interpreted language designed for the browser. Knowing one does not imply knowledge of the other.
Helpful but not mandatory — HTML gives you the structure JavaScript manipulates and CSS the styling it toggles. If you know basic tags, classes and selectors, you have enough to start; the DOM chapters assume only that much.
Use const by default and let when the value must change. Avoid var entirely in modern code: it is function-scoped instead of block-scoped, hoists differently, and can create accidental globals. This course uses const and let throughout.
The Document Object Model is the browser’s in-memory tree of objects representing your HTML. JavaScript reads and modifies that tree — selecting elements, changing text, adding nodes, attaching event listeners — and the browser re-renders the page to match.
== compares after converting types (so 5 == "5" is true), while === requires the same value AND the same type (5 === "5" is false). Loose equality has famously confusing rules — use === and !== everywhere unless you have a specific reason not to.
JavaScript is single-threaded, so long-running work (timers, fetches, events) is handled by the browser and its callbacks are queued. The event loop moves queued callbacks onto the call stack only when it is empty, with promise callbacks (microtasks) taking priority over timers. It is why asynchronous code never freezes the page.
A Promise is an object representing a future value — pending, fulfilled or rejected — that can be chained and combined (Promise.all). async/await is syntax sugar over promises that lets asynchronous code read top-to-bottom with ordinary try/catch error handling.
Yes. Frameworks are built on exactly these fundamentals — components are functions, state is your variable discipline, effects are events, rendering is DOM work. Developers who learn vanilla first understand what the framework is doing and debug far faster.
Type out the examples instead of copying, build small end-to-end projects (expense tracker, weather app, quiz), handle every UI state (loading, empty, success, error), and revisit fundamentals while reading real code. Consistency beats volume — a little most days outpaces occasional marathons.