Regular-function `this` is chosen by the invocation form, while arrows capture the surrounding binding. The `new` operator creates and links an instance, invokes a constructor with that instance, and selects the final result according to the constructor return value.
Reliable object APIs make receiver requirements visible, bind callbacks at their ownership boundary, enforce derived-constructor ordering, and use factories when creation is asynchronous or can fail normally.
JavaScript this keyword refers to the current object. In other words, while executing every JavaScript function has a reference to its current execution context, known as this. In simple, here execution context means how the function is called. JavaScript this has different values depending on where it is used-
Global Context: in a browser script, top-level this usually points to window. In JavaScript modules and strict-mode functions, the value is different and often undefined.
Object's Method: when a function is called with dot notation such as user.sayHi(), this points to the object before the dot.
The call(), apply() and bind() Methods: these methods let you explicitly choose the this value instead of depending on how the function is called.
The new keyword is used to create an instances of objects from a constructor function, which has to be placed before the constructor function call and will do the following-
function TutorialsLogic(tutorial) {
this.tutorial = tutorial;
}
const site = new TutorialsLogic('JavaScript');
console.log(site.tutorial); // JavaScript
console.log(site.__proto__ === TutorialsLogic.prototype); // true
The value of this depends entirely on how a function is called, not where it is defined.
// 1. Global context - this = window (browser) or global (Node.js)
console.log(this === window); // true (browser)
// 2. Object method - this = the object
const person = {
name: 'Alice',
greet() {
return `Hello, I am ${this.name}`;
}
};
console.log(person.greet()); // Hello, I am Alice
// 3. Regular function - this = undefined (strict) or window (non-strict)
function show() {
console.log(this); // window or undefined
}
// 4. Arrow function - this = inherited from enclosing scope
const timer = {
seconds: 0,
start() {
setInterval(() => {
this.seconds++; // 'this' refers to timer object
console.log(this.seconds);
}, 1000);
}
};
// 5. Event handler - this = the element that fired the event
document.getElementById('btn').addEventListener('click', function() {
console.log(this); // <button> element
});
These three methods let you explicitly set the value of this when calling a function.
function introduce(greeting, punctuation) {
return `${greeting}, I am ${this.name}${punctuation}`;
}
const user = { name: 'Bob' };
// call - invoke immediately, args passed individually
console.log(introduce.call(user, 'Hello', '!'));
// Hello, I am Bob!
// apply - invoke immediately, args passed as array
console.log(introduce.apply(user, ['Hi', '.']));
// Hi, I am Bob.
// bind - returns a NEW function with 'this' permanently bound
const boundIntro = introduce.bind(user, 'Hey');
console.log(boundIntro('?'));
// Hey, I am Bob?
// Practical: borrowing methods
const arr = [3, 1, 4, 1, 5, 9];
const max = Math.max.apply(null, arr);
console.log(max); // 9
// Modern equivalent:
console.log(Math.max(...arr)); // 9
A common mistake is saving a method into a variable and calling it later. The function is no longer called as an object method, so this no longer points to the original object. Use bind(), an arrow wrapper, or call the method through the object.
"use strict";
const account = {
owner: "Riya",
showOwner() {
return this.owner;
}
};
const looseFunction = account.showOwner;
// looseFunction(); // TypeError: this is undefined in strict mode
const fixedFunction = account.showOwner.bind(account);
console.log(fixedFunction()); // Riya
// Another safe option:
const wrapper = () => account.showOwner();
console.log(wrapper()); // Riya
When a constructor is called with new, JavaScript normally returns the newly created object. If the constructor explicitly returns another object, that object replaces the new instance. Returning a primitive value such as a string or number is ignored.
function User(name) {
this.name = name;
return "ignored";
}
console.log(new User("Aman").name); // Aman
function SpecialUser(name) {
this.name = name;
return { name: name, role: "admin" };
}
console.log(new SpecialUser("Maya")); // { name: "Maya", role: "admin" }
`this` is a binding supplied when a regular function is invoked. In `account.balance()`, the receiver before the dot becomes `this` for that call. The same function assigned to another object receives the other object, and a detached call such as `const read = account.balance; read()` loses the original receiver.
A standalone regular-function call receives `undefined` as `this` in strict mode. In a non-strict function, nullish `this` is substituted with the global object and primitive receivers are boxed. Modern modules and class bodies are strict, so code should not depend on global substitution.
`call` invokes with an explicit receiver and individual arguments; `apply` accepts an argument list; `bind` creates a new function with a fixed receiver and optionally leading arguments. Binding an already bound function does not replace its original receiver. Keep the bound function reference when it must later be removed as a listener.
Method ownership and receiver are different concepts. A method inherited through a prototype normally receives the object used at the call site, not the prototype where the function was defined. This is what lets one method operate on many instances.
An arrow function does not create its own `this`; it closes over the binding from the surrounding context. `call`, `apply`, and `bind` cannot replace that captured value. This makes an arrow useful for a nested callback that should retain the outer method receiver, but usually wrong as an object method that needs a dynamic receiver.
Callback APIs decide how regular callbacks are invoked. Array iteration methods normally call a callback without a useful receiver unless a `thisArg` is supplied. Promise handlers and timers should be treated the same way. Pass the required data explicitly or use a lexical arrow instead of assuming the callback remembers its source object.
A DOM listener registered with a normal function commonly receives the listener element as `this`, matching `event.currentTarget`; an arrow retains its outer binding. Prefer `currentTarget` when the element is part of the event contract because it is explicit and easier to type and test.
Class field arrows capture the instance during construction and remain bound when passed around, but each instance receives a separate function. Prototype methods are shared and can be bound only where needed. Choose based on identity, memory, override behavior, and callback ergonomics rather than making every method an arrow.
The `new` operator creates an object whose prototype is the constructor function prototype, invokes the constructor with that object as `this`, and normally returns the object. If the constructor explicitly returns another non-primitive object, that object replaces the created instance; returning a primitive does not replace it.
A constructable function and an ordinary callable function are related but distinct capabilities. Arrow functions and concise methods are not constructors and cannot be used with `new`. Classes require `new` and throw when called as ordinary functions, preventing accidental receiver loss.
The constructor property named `prototype` is not the same as the prototype of the constructor function itself. Instances created by `new C()` normally inherit from `C.prototype`. Replacing that object affects only later instances, while adding a method to the existing prototype is visible to instances sharing it.
`new.target` reports the constructor originally invoked with `new` and is undefined in an ordinary function call. A base constructor can use it to require construction or coordinate subclass behavior, though classes already enforce construction syntax more clearly.
A base class constructor receives the newly created instance as `this`. A derived constructor has no initialized `this` until `super()` successfully runs, so reading fields, calling instance methods, or returning implicitly before `super()` throws. Call `super()` once on every path that continues with the derived instance.
Instance field initializers run for each instance in construction order, while static fields and static blocks run with the class as `this`. Instance methods still use dynamic call-site receivers. Extracting `const save = editor.save` therefore loses the instance unless the method is bound or wrapped.
Private fields are checked against the receiver brand. Calling a class method with an unrelated object through `call` may fail even if that object has similarly named public fields. Do not expose methods as freely reusable callbacks when they require branded private state.
Prefer factories when creation can fail normally, return cached implementations, or choose among unrelated concrete types. Constructors should establish a valid instance synchronously. Avoid starting unowned asynchronous work inside a constructor; expose an async factory or explicit initialization method with clear failure and cleanup behavior.
When `this` is wrong, pause inside the function and inspect the exact invocation expression. Search for extraction, callback registration, destructuring, rebinding, proxy forwarding, and wrapper functions. The function definition alone cannot explain a regular function receiver.
Log constructor name or stable object identity only in temporary diagnostics; avoid serializing an entire receiver that may contain cycles or secrets. A stack trace plus call-site breakpoint usually reveals more than printing `this` after it has already been passed through wrappers.
APIs that do not need dynamic receivers are easier to compose when they accept dependencies and data as explicit parameters. Use `this` for cohesive object or class behavior, not as a hidden transport for arbitrary context. Document methods that must remain attached or bind them at the ownership boundary.
Tests should cover method calls, detached calls, bound calls, callbacks, subclass instances, constructor object returns, and attempts to construct non-constructable functions where relevant. Assert state and prototype relationships, not only one output value.
Explore 500+ free tutorials across 20+ languages and frameworks.