The this keyword is dynamically bound based on how a function is called — four binding rules (new, explicit, implicit, default) determine its value, with arrow functions being the exception that inherits this lexically.
In priority order: new (creates new object), explicit (call/apply/bind), implicit (object.method()), default (global or undefined in strict mode).
call() and apply() invoke immediately with explicit this (differ only in argument format); bind() returns a new permanently-bound function.
Arrow functions have no own this — they inherit it lexically from the enclosing scope, and call/apply/bind cannot override it.
Extracting a method from its object (e.g., passing as a callback) loses the implicit this binding — it falls through to default binding.
In strict mode, default binding gives undefined instead of the global object — this catches bugs where this is used accidentally.
Unlike most languages where this always refers to the current instance, JavaScript's this is determined at call time based on how a function is invoked. Understanding the four binding rules and their priority order is one of the most commonly tested JavaScript interview topics.
1. new Binding (Highest Priority)
When a function is called with new, JavaScript creates a new empty object, sets this to that object, executes the constructor, and returns this (unless the function explicitly returns a different object). Example: new Person('Alice') — this inside Person refers to the newly created object.
2. Explicit Binding
The call, apply, and bind methods explicitly set this:
func.call(thisArg, arg1, arg2) — calls the function immediately with this set to thisArg and arguments passed individually.func.apply(thisArg, [arg1, arg2]) — identical to call but arguments are passed as an array.func.bind(thisArg, arg1) — returns a new function with this permanently bound to thisArg. Does not call the function immediately. The bound this cannot be overridden by call or apply.3. Implicit Binding
When a function is called as a method on an object (obj.method()), this refers to the object to the left of the dot. The key detail: this binding is lost when you extract the method from the object. const fn = obj.method; fn(); — this is no longer obj, it falls through to default binding.
4. Default Binding (Lowest Priority)
When none of the above rules apply (standalone function call), this is the global object (window in browsers, globalThis in Node.js) in non-strict mode, or undefined in strict mode. This is the most common source of this-related bugs.
Arrow functions do not have their own this binding. They inherit this from the enclosing lexical scope at definition time. This cannot be changed — call(), apply(), and bind() have no effect on an arrow function's this. This makes arrow functions ideal for callbacks and event handlers where you want to preserve the surrounding context.
In DOM event listeners using regular functions, this refers to the element the listener is attached to (same as event.currentTarget). Using an arrow function, this inherits from the enclosing scope instead. In React class components, this distinction is why event handlers need explicit binding: this.handleClick = this.handleClick.bind(this) or using arrow function class fields.
Common Pitfall: Losing this in Callbacks
Passing an object method as a callback to setTimeout, addEventListener, or array methods extracts it from the object, losing the implicit binding. Three solutions: wrap in an arrow function (setTimeout(() => obj.method(), 100)), use .bind() (setTimeout(obj.method.bind(obj), 100)), or use arrow function class fields in modern code.
The binding rules apply in priority order: new > explicit (call/apply/bind) > implicit (obj.method()) > default (standalone call). Arrow functions bypass all rules by not having their own this. The most common interview question: "What does this refer to?" — the answer always depends on how the function is called, not where it is defined (unless it's an arrow function).
Fun Fact
The this keyword in JavaScript was inspired by Java and C++, but Brendan Eich made it dynamically bound rather than statically determined — a decision he has said was influenced by the need to support both object-oriented and functional programming styles in a language designed in 10 days.