In JavaScript, the this keyword is a fundamental concept that refers to the object it belongs to. Its value depends on the context in which it is used. Here’s a detailed explanation of how this works in different contexts:
1. Global Context
In the global execution context (outside of any function), this refers to the global object:
In a browser, it’s
window.In Node.js, it’s
global.
console.log(this); // In a browser, it logs `window`2. Function Context
In a regular function, the value of this depends on how the function is called.
Simple Function Call:
thisrefers to the global object (orundefinedin strict mode).function show() { console.log(this); } show(); // Logs `window` in non-strict mode, `undefined` in strict modeMethod Call: When a function is called as a method of an object,
thisrefers to the object the method is called on.const person = { name: 'Alice', greet() { console.log(this); // Logs `person` object } }; person.greet();
3. Constructor Function
When a function is used as a constructor with the new keyword, this refers to the newly created instance.
function Person(name) {
this.name = name;
}
const john = new Person('John');
console.log(john.name); // Logs 'John'4. Arrow Functions
Arrow functions do not have their own this. Instead, they inherit this from the enclosing lexical context (the function or block they are defined in).
const person = {
name: 'Alice',
greet: function() {
const innerFunc = () => {
console.log(this); // Logs `person` object
};
innerFunc();
}
};
person.greet();5. call, apply, and bind Methods
These methods allow explicitly setting the value of this.
callandapply: Immediately invoke the function with a specifiedthisvalue.function greet() { console.log(this.name); } const person = { name: 'Alice' }; greet.call(person); // Logs 'Alice' greet.apply(person); // Logs 'Alice'bind: Returns a new function, permanently settingthisto the provided value.const boundGreet = greet.bind(person); boundGreet(); // Logs 'Alice'
6. Event Handlers
In event handlers, this refers to the element that received the event.
document.getElementById('myButton').addEventListener('click', function() {
console.log(this); // Logs the button element
});<div class="infographic-thisctx">
<style>
.infographic-thisctx {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--good: #1a8a5f;
--good-soft: #e1f2ea;
--bad: #c8452e;
box-sizing: border-box;
max-width: 880px;
margin: 0 auto;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
overflow-x: hidden;
}
.infographic-thisctx[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--good: #4fbf8f;
--good-soft: #16281f;
--bad: #e0715a;
}
.infographic-thisctx * { box-sizing: border-box; }
.infographic-thisctx h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; margin: 0; }
.infographic-thisctx p { margin: 0; }
.infographic-thisctx button { font-family: inherit; }
.tc-wrap { padding: clamp(1.25rem, 5vw, 2.5rem) clamp(1.1rem, 5vw, 3rem) 2rem; }
.tc-hero { text-align: center; margin-bottom: 1.3rem; }
.tc-hero h1 { font-size: clamp(1.35rem, 4.5vw, 1.85rem); }
.tc-hero p { color: var(--ink-soft); margin-top: 0.45rem; font-size: 0.85rem; }
/* tabs */
.tc-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center; margin-bottom: 1.3rem; }
.tc-tab { border: 1px solid var(--line); background: var(--paper-alt); color: var(--ink-soft); padding: 0.4rem 0.75rem; font-size: 0.72rem; font-weight: 600; cursor: pointer; }
.tc-tab.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.tc-tab:hover:not(.is-active) { border-color: var(--ink-soft); color: var(--ink); }
.tc-panel { display: none; }
.tc-panel.is-active { display: block; }
.tc-code { border: 1.5px solid var(--line); background: var(--paper-alt); padding: 0.65rem 0.85rem; font-family: 'IBM Plex Mono', monospace; font-size: 0.72rem; line-height: 1.65; margin-bottom: 1.1rem; white-space: pre-wrap; color: var(--ink-soft); }
.tc-code strong { color: var(--ink); font-weight: 700; }
.tc-runrow { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; margin-bottom: 1.2rem; }
.tc-run { border: none; background: var(--accent); color: #fff; padding: 0.45rem 1rem; font-size: 0.76rem; font-weight: 600; cursor: pointer; }
.tc-run:active { transform: scale(0.96); }
.tc-run.alt { background: var(--good); }
.tc-targets { display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; min-height: 108px; margin-bottom: 0.4rem; }
.tc-target {
width: 170px; text-align: center; padding: 0.6rem 0.7rem;
border: 1.5px solid var(--line); background: var(--paper-alt);
opacity: 0; transform: scale(0.9) translateY(6px); position: relative;
transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.4s ease, background 0.4s ease;
}
.tc-target svg { width: 18px; height: 18px; stroke: var(--ink-soft); margin-bottom: 0.2rem; transition: stroke 0.4s ease; }
.tc-target .tc-tname { font-family: 'IBM Plex Mono', monospace; font-weight: 700; font-size: 0.74rem; }
.tc-target .tc-tsub { font-size: 0.6rem; color: var(--ink-soft); margin-top: 0.1rem; }
.tc-target .tc-mark { position: absolute; top: -9px; right: -9px; width: 20px; height: 20px; border-radius: 50%; background: var(--paper); border: 1.5px solid var(--line); display: flex; align-items: center; justify-content: center; font-size: 0.66rem; font-weight: 700; opacity: 0; transition: opacity 0.25s ease, border-color 0.25s ease, color 0.25s ease; }
.tc-target.is-hit { opacity: 1; transform: scale(1.04) translateY(0); border-color: var(--good); background: var(--good-soft); }
.tc-target.is-hit svg { stroke: var(--good); }
.tc-target.is-hit .tc-mark.hit { opacity: 1; color: var(--good); border-color: var(--good); }
.tc-target.is-miss { opacity: 0.45; transform: scale(0.96) translateY(0); }
.tc-target.is-miss .tc-mark.miss { opacity: 1; color: var(--bad); border-color: var(--bad); }
.tc-result {
text-align: center; font-size: 0.8rem; color: var(--ink-soft);
margin-top: 1.6rem; min-height: 1.4em;
opacity: 0; transition: opacity 0.35s ease;
}
.tc-result.is-shown { opacity: 1; }
.tc-result strong { color: var(--ink); }
.tc-tag { display: inline-block; margin-top: 0.35rem; font-size: 0.56rem; padding: 0.1rem 0.35rem; border: 1px solid var(--line); color: var(--ink-soft); opacity: 0; transition: opacity 0.3s ease; }
.tc-target.is-hit .tc-tag.show-when-hit { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
.infographic-thisctx *, .infographic-thisctx *::before, .infographic-thisctx *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="tc-wrap">
<div class="tc-hero">
<h1>What does "this" point to?</h1>
<p>Same word, six different answers depending on how the function is called.</p>
</div>
<div class="tc-tabs">
<button class="tc-tab is-active" data-tab="global" type="button">Global</button>
<button class="tc-tab" data-tab="function" type="button">Function call</button>
<button class="tc-tab" data-tab="ctor" type="button">Constructor</button>
<button class="tc-tab" data-tab="arrow" type="button">Arrow function</button>
<button class="tc-tab" data-tab="explicit" type="button">call / apply / bind</button>
<button class="tc-tab" data-tab="event" type="button">Event handler</button>
</div>
<!-- 1. Global -->
<div class="tc-panel is-active" data-panel="global">
<div class="tc-code"><strong>console.log(this);</strong></div>
<div class="tc-runrow"><button class="tc-run" data-run="global" type="button">Run</button></div>
<div class="tc-targets">
<div class="tc-target" id="t-global">
<div class="tc-mark hit">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.54 15H17a2 2 0 0 0-2 2v4.54"/><path d="M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17"/><path d="M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05"/><circle cx="12" cy="12" r="10"/></svg>
<div class="tc-tname">window / global</div>
<div class="tc-tsub">no function, no object — the top-level scope</div>
</div>
</div>
<p class="tc-result" id="r-global">Outside any function, "this" is the environment itself.</p>
</div>
<!-- 2. Function call -->
<div class="tc-panel" data-panel="function">
<div class="tc-code"><strong>function show() { console.log(this); }
show();</strong> <span>// plain call</span>
<strong>person.greet();</strong> <span>// method call</span></div>
<div class="tc-runrow">
<button class="tc-run" data-run="fn-plain" type="button">show()</button>
<button class="tc-run alt" data-run="fn-method" type="button">person.greet()</button>
</div>
<div class="tc-targets">
<div class="tc-target" id="t-fn-global">
<div class="tc-mark hit">✓</div><div class="tc-mark miss">✕</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.54 15H17a2 2 0 0 0-2 2v4.54"/><path d="M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17"/><path d="M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05"/><circle cx="12" cy="12" r="10"/></svg>
<div class="tc-tname">window (undefined in strict)</div>
<div class="tc-tsub">no object before the dot</div>
</div>
<div class="tc-target" id="t-fn-person">
<div class="tc-mark hit">✓</div><div class="tc-mark miss">✕</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<div class="tc-tname">person</div>
<div class="tc-tsub">the object before the dot</div>
</div>
</div>
<p class="tc-result" id="r-function">"this" follows the dot — whatever sits directly left of the call.</p>
</div>
<!-- 3. Constructor -->
<div class="tc-panel" data-panel="ctor">
<div class="tc-code"><strong>function Person(name) { this.name = name; }
const john = new Person("John");</strong></div>
<div class="tc-runrow"><button class="tc-run" data-run="ctor" type="button">new Person("John")</button></div>
<div class="tc-targets">
<div class="tc-target" id="t-ctor">
<div class="tc-mark hit">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>
<div class="tc-tname">john</div>
<div class="tc-tsub">the object "new" just created</div>
</div>
</div>
<p class="tc-result" id="r-ctor">With "new", "this" is the brand-new instance being built.</p>
</div>
<!-- 4. Arrow function -->
<div class="tc-panel" data-panel="arrow">
<div class="tc-code"><strong>greet() {
const inner = () => { console.log(this); };
inner();
}</strong></div>
<div class="tc-runrow"><button class="tc-run" data-run="arrow" type="button">person.greet()</button></div>
<div class="tc-targets">
<div class="tc-target" id="t-arrow-own">
<div class="tc-mark miss">✕</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z"/><rect x="3" y="14" width="7" height="7" rx="1"/><circle cx="17.5" cy="17.5" r="3.5"/></svg>
<div class="tc-tname">arrow's own this</div>
<div class="tc-tsub">doesn't exist — never a candidate</div>
</div>
<div class="tc-target" id="t-arrow-outer">
<div class="tc-mark hit">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<div class="tc-tname">person</div>
<div class="tc-tsub">this from the enclosing greet()</div>
</div>
</div>
<p class="tc-result" id="r-arrow">Arrow functions have no "this" slot — they just read the surrounding one.</p>
</div>
<!-- 5. call/apply/bind -->
<div class="tc-panel" data-panel="explicit">
<div class="tc-code"><strong>function greet() { console.log(this.name); }
const person = { name: "Alice" };</strong></div>
<div class="tc-runrow">
<button class="tc-run" data-run="ex-call" type="button">greet.call(person)</button>
<button class="tc-run alt" data-run="ex-apply" type="button">greet.apply(person)</button>
<button class="tc-run alt" data-run="ex-bind" type="button">greet.bind(person)()</button>
</div>
<div class="tc-targets">
<div class="tc-target" id="t-explicit">
<div class="tc-mark hit">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<div class="tc-tname">person</div>
<div class="tc-tsub" id="explicit-sub">you name the target directly</div>
<span class="tc-tag show-when-hit" id="explicit-tag"></span>
</div>
</div>
<p class="tc-result" id="r-explicit">Whatever the dot or arrow would have picked, these three override it.</p>
</div>
<!-- 6. Event handler -->
<div class="tc-panel" data-panel="event">
<div class="tc-code"><strong>button.addEventListener("click", function() {
console.log(this);
});</strong></div>
<div class="tc-runrow"><button class="tc-run" data-run="event" type="button">simulate click</button></div>
<div class="tc-targets">
<div class="tc-target" id="t-event">
<div class="tc-mark hit">✓</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4.1 12 6"/><path d="m5.1 8-2.9-.8"/><path d="m6 12-1.9 2"/><path d="M7.2 2.2 8 5.1"/><path d="M9.037 9.69a.498.498 0 0 1 .653-.653l11 4.5a.5.5 0 0 1-.074.949l-4.349 1.041a1 1 0 0 0-.74.739l-1.04 4.35a.5.5 0 0 1-.95.074z"/></svg>
<div class="tc-tname">the button</div>
<div class="tc-tsub">the element the event fired on</div>
</div>
</div>
<p class="tc-result" id="r-event">A plain function handler binds "this" to the element itself.</p>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-thisctx')
|| document.querySelector('.infographic-thisctx');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
/* tab switching */
wrapper.querySelectorAll('.tc-tab').forEach(tab => {
tab.addEventListener('click', () => {
wrapper.querySelectorAll('.tc-tab').forEach(t => t.classList.remove('is-active'));
wrapper.querySelectorAll('.tc-panel').forEach(p => p.classList.remove('is-active'));
tab.classList.add('is-active');
const panel = wrapper.querySelector('.tc-panel[data-panel="' + tab.dataset.tab + '"]');
panel.classList.add('is-active');
panel.querySelectorAll('.tc-target').forEach(t => t.classList.remove('is-hit', 'is-miss'));
const result = panel.querySelector('.tc-result');
if (result) result.classList.remove('is-shown');
});
});
function hit(el) { el.classList.remove('is-miss'); el.classList.add('is-hit'); }
function miss(el) { el.classList.remove('is-hit'); el.classList.add('is-miss'); }
function clear(el) { el.classList.remove('is-hit', 'is-miss'); }
function showResult(btn) {
const panel = btn.closest('.tc-panel');
const result = panel.querySelector('.tc-result');
result.classList.remove('is-shown');
void result.offsetWidth;
result.classList.add('is-shown');
}
wrapper.querySelectorAll('.tc-run').forEach(btn => {
btn.addEventListener('click', () => {
const kind = btn.dataset.run;
showResult(btn);
if (kind === 'global') {
hit(wrapper.querySelector('#t-global'));
}
if (kind === 'fn-plain') {
void wrapper.querySelector('#t-fn-global').offsetWidth;
hit(wrapper.querySelector('#t-fn-global'));
miss(wrapper.querySelector('#t-fn-person'));
}
if (kind === 'fn-method') {
hit(wrapper.querySelector('#t-fn-person'));
miss(wrapper.querySelector('#t-fn-global'));
}
if (kind === 'ctor') {
hit(wrapper.querySelector('#t-ctor'));
}
if (kind === 'arrow') {
hit(wrapper.querySelector('#t-arrow-outer'));
miss(wrapper.querySelector('#t-arrow-own'));
}
if (kind === 'ex-call' || kind === 'ex-apply' || kind === 'ex-bind') {
const target = wrapper.querySelector('#t-explicit');
const sub = wrapper.querySelector('#explicit-sub');
const tag = wrapper.querySelector('#explicit-tag');
clear(target);
void target.offsetWidth;
hit(target);
if (kind === 'ex-call') { sub.textContent = 'set immediately by .call()'; tag.textContent = 'ran now'; }
if (kind === 'ex-apply') { sub.textContent = 'set immediately by .apply()'; tag.textContent = 'ran now'; }
if (kind === 'ex-bind') { sub.textContent = 'locked in by .bind(), then run'; tag.textContent = 'bound, then called'; }
}
if (kind === 'event') {
hit(wrapper.querySelector('#t-event'));
}
});
});
})();
</script>Summary Table
Context |
|
|---|---|
Global | Global object ( |
Function (non-strict mode) | Global object ( |
Function (strict mode) |
|
Method of an object | The object itself |
Constructor function | The newly created instance |
Arrow function | Lexical context |
| Explicitly specified object |
Event handler | The element receiving the event |
Understanding the behavior of this is crucial for writing effective and bug-free JavaScript code, especially when working with object-oriented programming and event handling.