All About JavaScript
1. this keyword
Golden Rule: this depends on how the function is called, not where the function is written (arrow function is an exception)
Means if same function is called by different ways then it will become this . That’s why people get confused, so always focus on how the function is called ?
a. this in Global Scope (browser vs node)
In the browser, at the top level, this refers to the entire window object:
console.log(this); // window (the entire browser global object)In Node.js (inside a .js file), every file is a module; therefore, at the top level, this is an empty object:
console.log(this); // {} (this is actually module.exports)Note: In the Node REPL (accessed by simply typing node in the terminal), this refers to global, whereas inside a .js file, this is {} (module.exports). You will observe this difference if you test it in both environments.
1.2 — `this` in Regular Functions
function show() {
console.log(this);
}
show();- Browser (non-strict):
this=window - Node (non-strict):
this=global - In strict mode:
this=undefined
Why? When you call a function directly like show() without an object, JavaScript assigns the default global object. This is called default binding.
1.3 — `this` in Methods
When a function is inside an object and called via that object:
const user = {
name: "Bikram",
greet() {
console.log("Hi, " + this.name);
}
};
user.greet(); // Hi, BikramTrick: “Look to the left of the dot — whatever is there, that is this.”
user.greet()→useris to the left of the dot →this = user.
Here is the catch (important demo):
const fn = user.greet; // extracted the reference, left the object behind
fn(); // undefined (now there is no object to the left of the dot)It is the same function, but now there is no object to the left of the dot so this became the global object. This proves that this depends on “how it is called,” not “where it is written.”
1.4 — `this` in Arrow Functions (Lexical `this`)
An arrow function does not have its own this. It borrows this from its surroundings (enclosing scope). This is called lexical this.
const user = {
name: "Bikram",
greet: () => {
console.log(this.name); // undefined! (didn't take it from user)
}
};
user.greet();However, this arrow function proves particularly useful in one specific scenario when dealing with nested callbacks:
const user = {
name: "Bikram",
hobbies: ["coding", "gaming"],
show() {
this.hobbies.forEach((hobby) => {
// The arrow function borrows 'this' from show()
console.log(this.name + " likes " + hobby);
});
}
};
user.show();
// Bikram likes coding
// Bikram likes gamingRule of thumb: Always define methods as regular functions (so that
thispoints to the object), and use arrow functions for callbacks inside those methods (so thatthisis preserved).
1.5 — `this` in Event Handlers
In a regular function handler, this refers to the HTML element that triggered the event:
button.addEventListener("click", function () {
console.log(this); // The button element that was clicked
});In an arrow function handler, this does not point to the element it points to the lexical scope (the window object):
button.addEventListener("click", () => {
console.log(this); // window (NOT the button)
});Practical Tip: If you need access to the clicked element inside the handler, use a regular function or use
event.targetthe latter works in both cases.
1.6 — `this` in Strict Mode
Using "use strict" makes JavaScript behave more strictly. The biggest change is:
"use strict";
function show() {
console.log(this); // undefined (in non-strict mode, it would be window)
}
show();In non-strict mode, default binding points to the global object; in strict mode, it results in undefined. This is good it prevents accidental pollution of the global object. (ES6 modules and class bodies are automatically in strict mode.)
Quick Summary — `this` Cheatsheet
2. `call`, `apply`, `bind` Explicit Binding
Sometimes we want to decide for ourselves what `this` refers to. There are 3 methods for this known as explicit binding.
function introduce(city, country) {
console.log(`I am ${this.name}, from ${city}, ${country}`);
}
const person = { name: "Bikram" };`call`: calls immediately, arguments separated by commas
introduce.call(person, "Kolkata", "India");
// I am Bikram, from Kolkata, IndiaFirst argument = value of `this`. Remaining arguments are standard comma separated values.
`apply` — call immediately, arguments in an ARRAY
introduce.apply(person, ["Kolkata", "India"]);
// I am Bikram, from Kolkata, IndiaMnemonic: Apply = Array || Call = Comma. (A for A, C for C.)
`bind` do NOT call immediately; returns a new function
const boundFn = introduce.bind(person);
boundFn("Kolkata", "India");
// I am Bikram, from Kolkata, India`bind` does not call the function immediately it returns a new function where `this` is permanently fixed (locked) to `person`. You can call it later whenever you want.
Real-world use: In event handlers and callbacks where the `
this` context might get lost, `bind` is used to fix it permanently. For example: `setTimeout(introduce.bind(person), 1000)`.
3. Prototypes
3.1: What is a prototype?
Every JavaScript object contains a hidden link pointing to another object. That other object is called its prototype.
Benefit: If a property or method isn’t found in the object, JS looks for it in its prototype; if it’s not found there either, it checks the prototype’s prototype… and so on, moving up the chain. This chain is known as the prototype chain. Think of it like this: if a child can’t find something, they ask their father; if the father doesn’t have it, they ask the grandfather.
3.2: `__proto__` vs `prototype` (The most confusing pair)
Proof via code:
function Person(name) {
this.name = name;
}
const p = new Person("Bikram");
console.log(p.__proto__ === Person.prototype); // trueIn a nutshell: `
prototype` = a property of the function (blueprint for instances). `__proto__` = a property of the instance (the path to its blueprint). Connection: `instance.__proto__ === Function.prototype`.Note: `
__proto__` is considered “legacy” today (though it’s great for understanding concepts). In production code, `Object.getPrototypeOf(obj)` is used instead.
3.3: Prototype Chain
const arr = [1, 2, 3];
arr.push(4); // Where did push come from? It isn't defined inside arr!JS searched like this:
- In `
arr` → didn’t find it - In `
arr.__proto__` (which is `Array.prototype`) → found it!
arr → Array.prototype → Object.prototype → null
Get Bikramjit Roy’s stories in your inbox
Join Medium for free to get updates from this writer.
When it reaches `null` without finding the item → `undefined` is returned. This is the prototype chain.
3.4: `Object.create`
This method directly creates an object where you set the prototype yourself:
const animal = {
eats: true,
walk() { console.log("Animal is walking"); }
};
const dog = Object.create(animal); // dog's prototype = animal
dog.barks = true;
console.log(dog.barks); // true (its own)
console.log(dog.eats); // true (inherited from animal!)
dog.walk(); // Animal is walking3.5: Inheritance via Prototypes
const vehicle = {
start() { console.log(this.name + " has started"); }
};
const car = Object.create(vehicle);
car.name = "Swift";
car.start(); // Swift has started`car` inherited the `start` method from `vehicle`, and when the method ran, `this` referred to `car` (the “left-of-the-dot” rule!). That is why `this.name` became “Swift”. This is the core idea of prototypal inheritance.
4. ES6 Classes
Understand first: new keyword comes with Class. When new MyClass() runs, JS does 4 things:
(1) a new empty object is created,
(2) its proto is set to class’s prototype,
(3) constructor runs in which this = that new object,
(4) that object automatically gets returned.
4.1: class Syntax + constructor + Instance Methods
class Person {
// this automatically gets called on running new
constructor(name, age) {
this.name = name;
this.age = age;
}
// instance method → automatically goes to prototype
greet() {
console.log("Hi, main " + this.name);
}
}
const p = new Person("Bikram", 27);
p.greet(); // Hi, I am Bikramconstructor → setup work (assign properties). methods like
greet() → automatically go to Person.prototype (copy is not created in every object — saves memory).
4.2 — Inheritance: extends & super
class Animal {
constructor(name) { this.name = name; }
eat() { console.log(this.name + " is eating"); }
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // parent constructor call
this.breed = breed;
}
bark() { console.log(this.name + " bhonk raha hai"); }
}
const d = new Dog("Tommy", "Labrador");
d.eat(); // Tommy is eating (inherited from Animal)
d.bark(); // Tommy is barking (Dog's own)extends → makes one class inherit from another.
super(name) → calls parent’s constructor.
⚠️ Rule: Calling super() before using this in child constructor is necessary, otherwise error. You can also call parent’s method using super.methodName().
4.3 — Static Methods & Static Properties static things are not on object, they are on class. These are not called from instance, but directly from class.
class MathHelper {
static PI = 3.14159; // static property
static add(a, b) { return a + b; } // static method
}
console.log(MathHelper.add(2, 3)); // 5 (call from class)
console.log(MathHelper.PI); // 3.14159
const m = new MathHelper();
// m.add(2, 3); → Error: static method is not on instance📌 Real example: Array.isArray() is a static method. You don’t call it like [].isArray(), but like Array.isArray([]). Static is perfect for utility/helper functions.
4.4 — Getters & Setters
Through these we use a method like a property (without putting ()).
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
get fullName() { // getter
return this.first + " " + this.last;
}
set fullName(value) { // setter
const parts = value.split(" ");
this.first = parts[0];
this.last = parts[1];
}
}
const p = new Person("Bikram", "Roy");
console.log(p.fullName); // Bikram Roy ((without ()!)
p.fullName = "Dipi Das"; // setter ran
console.log(p.first); // DipiPay attention — wrote p.fullName, not p.fullName(). Getter/setter give feeling like property but logic runs inside. Very useful for validation (you can put check in setter).
4.5: Private Fields (#)
By applying # field cannot be accessed from outside only inside class.
class BankAccount {
#balance = 0; // private field
deposit(amount) { this.#balance += amount; }
getBalance() { return this.#balance; }
}
const acc = new BankAccount();
acc.deposit(500);
console.log(acc.getBalance()); // 500
// console.log(acc.#balance); → SyntaxError: no access from outside!Only methods inside class can touch #balance. This gives encapsulation — saves important data from wrong changes.
4.6: Big Reveal: Classes are just Syntactic Sugar
“Class” is not any new thing in JavaScript. Inside it is that same old prototype + constructor function, just looks beautiful. This is called “syntactic sugar” — sweet packaging, same thing inside.
Proof 1 — Class is actually a function itself:
class Person {
constructor(name) { this.name = name; }
greet() { return "Hi " + this.name; }
}
console.log(typeof Person); // "function" (not class, function!)Proof 2 — Methods went onto prototype itself:
console.log(typeof Person.prototype.greet); // "function"
// meaning greet method lies on Person.prototype - like old wayProof 3 — Instance link is the same:
const p = new Person("Bikram");
console.log(p.__proto__ === Person.prototype); // trueProof 4 — Exactly same thing in old way:
// This class...
class Person {
constructor(name) { this.name = name; }
greet() { return "Hi " + this.name; }
}
// ...is equal to exactly this constructor function:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return "Hi " + this.name;
};Behavior of both is 100% same. class is just an easy and clean way of writing inside, JavaScript is using prototype itself.
Remember: Next time you see any class, remember hidden behind is that same prototype that you studied in beginning. JavaScript has just dressed it in beautiful clothes.








