Introduction (JavaScript & EcmaScript)
JavaScript is a programming language used to build interactive and dynamic websites. It works alongside HTML and CSS to control a webpage's behavior, such as:
- Showing or hiding elements
- Validating form inputs
- Fetching data from servers
- Creating animations
- Handling user events like clicks or keystrokes
JavaScript code is executed directly in the web browser, making it a client-side language. However, with environments like Node.js, JavaScript can also run on servers (server-side).
Key Uses of JavaScript:
- Web interactivity: Slideshows, modals, dropdowns, etc.
- Single Page Applications (SPAs): Using frameworks like React, Vue, or Angular.
- Server-side development: With Node.js (e.g., building APIs).
- Mobile & Desktop Apps: Using tools like React Native or Electron.
- Game Development: Lightweight browser games using canvas and WebGL.
What is ECMAScript (ES)?
ECMAScript (ES) is the standard specification for JavaScript, defined by ECMA International. JavaScript is an implementation of this specification. ECMAScript defines:
- Syntax rules
- Core features (like variables, functions, classes, modules)
- New updates (ES6, ES7, ES2020, etc.)
- For example: let, const, arrow functions (()=>{}), template strings (`Hello ${name}`), and class syntax were introduced in ES6 (2015).
Example:
// example of onClick event handling (in a button)
<button onclick="greet()">Click Me</button>
<script>
function greet() {
alert('Hello, world!');
}
</script>
When the user clicks the button, the JavaScript greet() function runs and shows an
alert.
Creating Variables
A variable in JavaScript is used to store data that you can reuse or modify later.
Declaring Variables
You can declare variables using:
- var (old, mostly avoided now)
- let (modern, block-scoped). Use it if you need to re-assign the value later.
- const (modern, block-scoped and constant). Use it if the value doesn't need to re-assign later.
Best practice for variables
- Give meaningful names to variables
- Don't start names with [0-9]
- Don't use reserved keywords. Example: this, for, if, class, …
- Allowed keys: [aA-zZ], [0-9], and _ (underscore)
- Variable names are case sensitive so make sure you name them properly and use proper identifier.
- Best practice is to declare one variable per line.
Types:
Value Types:
- string
- number
- boolean
- undefined
- null
Referece Types:
- objects
- arrays
Statically typed Vs. Dynamically typed:
- Statically typed language need to declare variables along with their types
- Dynamically typed language can change their variable type during run time.
Variable scope:
- var: function scoped
- let/const: block scoped (conditional block, loop block, function)
typeof Operator
Use typeof to check the type of a variable.
Hoisting:
Variables and functions are moved to the top of their scope before code execution (JS moves declarations to the top).
Function expressions are not hoisted with value
Closures
A closure formed is when a function remembers the variables from its outer scope, even after that scope has closed.
Closures Are Used In: Data privacy (like private variables), Factory functions, Event handlers, setTimeout, setInterval, React hooks (especially useState and useEffect)
Example:
// Hoisting example.
sayHello(); // ❌ TypeError: sayHello is not a function
// sayHello is a function expression.
var sayHello = function() {
console.log("Hello");
};
// closure example:
function outer() {
let count = 0;
return function inner() {
count++;
console.log(count);
};
}
const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
Operators
An operator is a symbol used to perform operations on variables and values — like arithmetic, comparison, logic, etc.
Arithmetic Operators
Used for basic math
- + (addition)
- - (substraction)
- * (multiplication)
- / (division): 5 / 2 = 2.5
- % (modulus): 5 % 2 = 1
- ** (exponentiation): 2 ** 3 = 8
- ++ (increment)
- -- (decrement)
Assignment Operators
Assign values to variables
- =
- +=
- -=
- *=
- /=
- %=
- **=
Comparison Operators:
Return true or false.
- == (equal loose)
- === (equal strict)
- != (not equal)
- !==
- >, <,>=, <=
Logical Operators:
Used to combine conditions.
- && (AND)
- || (OR)
Type Operators
- typeof
- instanceof
String Operators:
- + : let fullName = firstName + ' ' + 'lastName;
Ternary Operator (? :)
let result = condition ? true_value : default_or_false_value;
Nullish Coalescing Operator (??)
Returns the right-hand value if the left-hand is null or undefined.
let name = null ?? "Guest"; // "Guest"
let count = 0 ?? 10; // 0 ✅ (not null)
Optional Chaining (?.)
Safely access nested properties without errors.
Spread (...) and Rest (...) Operators
let arr1 = [1, 2];
let arr2 = [...arr1, 3]; // [1, 2, 3];
function sum(...nums) {
return nums.reduce((a, b) => a + b);
}
Control Flow (Loops and Conditionals)
Control Flow is the order in which code is executed in a program. JavaScript runs code from top to bottom, unless you change the flow using conditions, loops, or jumps.
Conditional Statements
Used to execute different blocks of code based on conditions.
- if
- else if
- else
- switch
Loops
- for
- while
- do...while
Loop Control: break and continue:
- break → exits the loop early
- continue → skips current iteration
Early Return in Functions:
Exit early to avoid unnecessary logic.
&& and || trick
- &&: execute only if the condition matches. Example: condition && execute_this_block_or_func
- ||: execute default block or func if the condition isn't met. Example: condition || default_call_or_val
Example:
let isLoggedIn = true;
isLoggedIn && console.log("Welcome!"); // shortcut for if
let userName = null;
let displayName = userName || "Guest"; // fallback value
let age = 18;
let status = (age >= 18) ? "Adult" : "Minor";
Note: in loops, make sure to set a proper condition to exit. Otherwise, infinite loop can
happen.
Arrow Function
Arrow functions (=>) are a shorter way to write functions in JavaScript introduced in ES6 (2015). But they're not just shorter — they behave differently, especially with keyword this.
// basic function!
function greet(name) {
return "Hello " + name;
}
// arrow function.
const greet = (name) => {
return "Hello " + name;
};
// or even shorter.
const greet = name => "Hellow " + name;
// multiple parameters.
const add = (a, b) => a + b;
// no parameters
const sayHi = () => "Hi!";
// Multiline Function Body
const multiply = (a, b) => {
const result = a * b;
return result;
};
Some Information:
- If there's only one parameter, you can omit parentheses.
- If there's only one expression (function body), you can omit {} and return.
- Always use () if you have 0 or more than 1 parameter.
- In a block body ({}), you MUST use return explicitly.
Important: this Binding Difference
- Arrow functions do NOT have their own this.
- They inherit this from the surrounding scope (called lexical this)
// regular 'this'
const person = {
name: "Bharat",
greet: function() {
console.log("Hi, I'm " + this.name); // ✅ this = person
}
};
// arrow 'this' (won't work)
const person = {
name: "Bharat",
greet: () => {
console.log("Hi, I'm " + this.name); // ❌ this = undefined / window
}
};
// correct use of 'this': notice we wrapped in regular function (where 'this' refers to object).
const person = {
name: "Bharat",
greet: function() {
setTimeout(() => {
console.log("Hi " + this.name); // ✅ this = person
}, 1000);
}
};
Common Use Cases of Arrow Functions:
- Callbacks
- In .map(), .filter(), .reduce()
Where NOT to Use Arrow Functions:
- Object methods: this will be wrong
- Constructors (new): Arrow funcs can’t be used as constructors
- Prototype methods: Same issue with as object methods.
- Event listeners (sometimes): You may need correct this (e.g., DOM element)
Use Case Tips
- Inside methods: use regular functions
- Inside setTimeout, setInterval: use arrow if you want this from parent
- Implicit return of object literals → wrap in ()
const user = {
name: "Ram",
greet() {
console.log(this.name); // ✅ works
}
};
setTimeout(() => {
console.log(this); // parent context
}, 1000);
// if you need to return an object from an one liner arrow function, user ().
const getUser = () => ({ name: "Bharat", age: 30 });
Note: "Lexical" means "defined by position in the source code".
Destructuring
Destructuring is a syntax in JavaScript that lets you unpack values from arrays or properties from objects into separate variables — in one line.
Array Destructuring
const numbers = [10, 20, 30];
const [a, b, c] = numbers;
console.log(a); // 10
console.log(b); // 20
// Skipping Items
const [first, , third] = [1, 2, 3];
console.log(first); // 1
console.log(third); // 3
// Swapping Values
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2, 1
// With Rest Operator
const [head, ...rest] = [1, 2, 3, 4];
console.log(head); // 1
console.log(rest); // [2, 3, 4]
Object Destructuring:
const user = { name: "Bharat", age: 25 };
const { name, age } = user;
console.log(name); // "Bharat"
console.log(age); // 25
// Renaming Variables
const { name: fullName, age: years } = user;
console.log(fullName); // "Bharat"
// Default Values
const { country = "Nepal" } = user;
console.log(country); // "Nepal"
// Nested Destructuring
const user = {
name: "Ram",
address: {
city: "Kathmandu",
zip: 44600
}
};
const { address: { city, zip } } = user;
console.log(city); // "Kathmandu"
// Destructuring in Function Parameters (React Component props)
function greet({ name, age }) {
console.log(`Hello ${name}, age ${age}`);
}
const person = { name: "Hari", age: 30 };
greet(person); // Hello Hari, age 30
// Destructuring in Loops
const users = [
{ id: 1, name: "Bharat" },
{ id: 2, name: "Thapa" }
];
for (const { id, name } of users) {
console.log(`${id}: ${name}`);
}
Tips:
- object destructuring is based on property names, not position so order doesn't matter.
- Variable names must match property names (unless you rename) (for object destructuring)
- Arrays are ordered collections, so destructuring array is based on index position.
- You can skip values using commas (in array destructuring)
'this' Keyword
In JavaScript, this is a keyword that refers to the object that is executing the current function.
Its value depends on how and where the function is called, not just where it's written.
Notes:
- Value of 'this' in Global scope:
- strict mode: undefined
- non strict mode: window (in browser) and global in node env.
- Inside object's method 'this' refers to the object.
- if you call 'this' from normal function, 'this' refers to global object (window or global)
- 'this' inside Event listener refers to the element that fired the event.
- When this is called inside methods that are not part of the object, it will reference the global object.
// this in Global Scope
console.log(this); // In browser: window
// strict mode using 'use strict'
"use strict";
console.log(this); // undefined
// this in Functions
function show() {
console.log(this);
}
show(); // non-strict: window | strict: undefined
// this Inside an Object Method
const person = {
name: "Bharat",
greet() {
console.log(this.name);
}
};
person.greet(); // "Bharat" ✅ (this = person)
// this with Arrow Functions
const person = {
name: "Bharat",
greet: () => {
console.log(this.name);
}
};
person.greet(); // ❌ undefined (arrow function has no `this`)
// 'this' called from methods that are not part of the object.
const video = {
title: 'Video Title',
tags: [ 'a', 'b', 'c', 'd' ],
play: function(){
console.log( 'this inside play', this ); // will reference video!
},
showTags: function(){
this.tags.forEach( function( tag ){
// this will reference the global object in this case!
// if you use arrow function here, it will inherit the scope from parent method "showtags" and 'this' work correctly
console.log( 'this inside forEach method', this );
} );
}
};
// this in Constructor Functions
function Person(name) {
this.name = name;
this.greet = function() {
console.log( 'hello ' + name );
};
}
const p = new Person("John");
console.log(p.name); // "John" (this refers to the new object being created)
p.greet(); // references Person
const newFunction = p.greet;
newFunction(); // this call is not part of the object now (i.e., like object.funcName() ) and 'this' won't refer to Person obj. It refers to global/window obj.
// to fix above : option 1 is to use .bind()
const newFunction = p.greet.bind(p);
newFunction(); // hello John
// option 2 is to use arrow function in Person constructor function.
this.greet = () => console.log( 'hello ' + name);
// now without even the .bind() call, newFunction() will work as expected.
// this in Classes
class Car {
constructor(name) {
this.name = name;
}
start() {
console.log(`${this.name} started`);
}
}
const c = new Car("Toyota");
c.start(); // "Toyota started"
this with call(), apply(), and bind()
Used to manually control what 'this' refers to.
- call(): Calls the function immediately, and lets you pass arguments one-by-one.
- apply(): Like call(), but arguments are passed as an array.
- bind(): Returns a new function with this permanently set — but does NOT call it immediately.
- Once bound using bind(), this cannot be changed again using call or apply.
// call() → immediately calls with custom this
function greet() {
console.log(this.name);
}
greet.call({ name: "John" }); // "John"
// apply() → like call() but with arguments as array
greet.apply({ name: "Ram" }); // "Ram"
// bind() → returns a new function with bound this
const bound = greet.bind({ name: "Hari" });
bound(); // "Hari"
'this' in DOM Event Handlers
document.querySelector("button").addEventListener("click", function () {
console.log(this); // ✅ the <button> element
});
// But with arrow function:
document.querySelector("button").addEventListener("click", () => {
console.log(this); // ❌ not button, inherited from outer scope (usually window)
});
Note: Use arrow functions only when you want to preserve this
Objects
A JavaScript object is a collection of key-value pairs (properties + methods).
Creating Objects:
- Object literal
- new Object()
- Factory function (returns obj structured from arguments)
- Constructor function (uses 'this' keyword)
- Class syntax
// object example:
const user = {
name: "Bharat",
age: 25,
greet() {
console.log(`Hello, ${this.name}`);
}
};
// Object literal
const person = { name: "Ram", age: 30 };
// Note: you can't use arrow function with 'this' to define a method inside an object literal.
// like:
// const person = { ..., someFunc: () => .. accessing 'this' here (fxn. body) is not correct., }; // 'this' won't refer to the object.
// good:
const person = {
name: 'John',
greet: function() {
console.log( 'hello ' + this.name );
}
};
// or:
const person = {
name: 'John',
greet: () => console.log( 'hello ' + person.name ) // using this.name would be wrong!
};
// creating object using 'new' keyword
const person = new Object();
person.name = "Ram";
// Factory function
function createUser(name, age) {
return { name, age };
}
// Constructor function
// first empty obj {} is created. value of 'this' is made to point to that object. Finally object is returned (implicitly).
function Person(name) {
this.name = name;
}
const p = new Person("John");
// class syntax
class Car {
constructor(brand) {
this.brand = brand;
}
}
Accessing Properties & Methods
- Dot notation: user.name;
- Bracket notation: user["name"];
- can also access special keys: user["full-name"]
- can also access dynamic keys: user[keyName]
- Calling methods: objectName.funcName();
- Objects are Dynamic in Nature: We can add, delete, and update object properties and methods by default.
- Delete property: delete user.age;
- Check existence: 'in' keyword ('propertyName' in objectName) or objectName.hasOwnProperty('propName') syntax.
- Looping through objects: for .. in loop.
- Use Object.keys(), Object.values(), or Object.entries() for better control
- Object Utility Methods: .keys(obj), .values(), .entries(), .assign(), .freeze(), .seal(), .hasOwn()
- Object.keys(obj): Returns array of keys
- Object.values(obj): Returns array of values
- Object.entries(obj): Returns array of [key, value]
- Object.assign(target, src): Copies properties
- Object.freeze(obj): Makes object immutable
- Object.seal(obj): Prevents adding/removing properties
- Object.hasOwn(obj, key): Safe key check
// Check existence
"name" in user; // true
user.hasOwnProperty("name"); // true
// looping obj.
for (let key in user) {
console.log(key, user[key]);
}
Object.keys(user); // ['name', 'city']
Object.values(user); // ['Bharat', 'Kathmandu']
Object.entries(user); // [['name', 'Bharat'], ['city', 'Kathmandu']]
// dynamic key
const key = "city";
const user = {
[key]: "Lalitpur"
};
Constructor Property and Prototype
Every object in JavaScript has a property called constructor that references the function used to create that object.
All objects in JS inherit from a hidden object called [[Prototype]] (accessed via __proto__ or Object.getPrototypeOf()).
When it (constructor property) tries to determine the function, first it looks for a custom constructor (as in constructor function for creating objects) and if there is no custom constructor function used then, it will return the native (built-in) function used to create that object.
const obj = {}; // constructor: Object()
const str = 'string val'; // constructor: String()
const num = 123; // constructor: Number()
…
const obj = {};
console.log(obj.constructor); // ƒ Object() {...}
const arr = [];
console.log(arr.constructor); // ƒ Array() {...}
// So, the .constructor tells you: "What function was used to create this object?"
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
// Custom Constructor
function Person(name) {
this.name = name;
}
const p = new Person("Bharat");
console.log(p.constructor); // ƒ Person(name) {...}
// The object p was created using Person(), so its .constructor points to Person.
// Recreate Objects
function clone(obj) {
return new obj.constructor();
}
const date = new Date();
const newDate = clone(date);
// Restore Constructor after Prototype Override
// correct approach:
Person.prototype = {
constructor: Person,
greet() {
console.log("Hi");
}
};
// incorrect approach:
function Person() {}
Person.prototype = {
greet() {
console.log("Hi");
}
};
const p = new Person();
console.log(p.constructor); // ❌ Object (not Person)
Functions
A function is a reusable block of code designed to perform a particular task.
Notes:
- Functions are Hoisted — can be called before definition.
- Function expressions are Not hoisted — only usable after definition.
- Without return, function returns undefined
- Functions create local scope.
- Recursion: A function that calls itself.
- First-Class Functions: JS Functions are first class. Meaning they can be:
- assigned to variables,
- passed as arguments,
- returned from other functions.
- Higher-Order Functions: A function that takes another function as an argument or returns a function. Example: map, filter, forEach, setTimeout
// example of a simple function
add(1, 2); // valid because of hoisting
function add(a, b){
return a + b;
}
add(2, 4);
// function expression (anonymous)
addNums(2, 3); // invalid because "function expression are not hoisted"
const addNums = function(a, b) {
return a + b;
};
addNums(1, 3); //valid
// function expression (named).
const addNamed = function add(a, b){
return a + b;
}
addNamed(2, 3); // valid
const sum = addNamed;
sum(3, 4); // valid because JS functions are first class.
// Here you can also use arrow functions in fxn. expressions.
Function Expression Vs. Function Declaration
- In function expression, you can't call the function before it's definition.
- It's because JavaScript doesn't hoist variables. And function expressions are just like variable definitions except that the value is a function type instead of value (number, string, …).
Hoisting
Process in which JavaScript moves all the function definitions to the top of the file. With this process, it's perfectly okay to call the function before its declaration unlike function expressions which are similar to variables
Note: ES6 classes are not hoisted similar to variables
Function Arguments and The Rest Operator
argument is a property available inside functions that holds all the arguments supplied to that function.
function sum(){
console.log( arguments );
}
// 'arguments' Object
function show() {
console.log(arguments);
}
show(1, 2, 3); // [1, 2, 3]
// Note: arguments object is not available in arrow functions.
// Rest Parameters
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
IIFE (Immediately Invoked Function Expression)
Used to create private scopes in older JS
(function() {
console.log("Runs immediately");
})();
// bind(), call(), apply()
greet.call(obj, arg1, arg2);
greet.apply(obj, [arg1, arg2]);
const fn = greet.bind(obj);
fn();
// Function Length & Name
function greet(name, age) {}
console.log(greet.length); // 2 (number of declared params)
console.log(greet.name); // "greet"
Getters and Setters
They're special functions that look like properties — used to get and set values in a clean, controlled way.
They allow you to run logic when accessing or changing an object's property.
Why Use Them?
- Hide internal details
- Add validation
- Make computed values look like regular properties
- Create reactive behavior (used heavily in frameworks like Vue, MobX)
Getter: Must return a value
Setter: Must take exactly 1 parameter
Getter & Setter work in Object literals & ES6 classes
const user = {
firstName: "Bharat",
lastName: "Thapa",
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(value) {
const [first, last] = value.split(" ");
this.firstName = first;
this.lastName = last;
}
};
console.log(user.fullName); // "Bharat Thapa" ✅
user.fullName = "Ram Bahadur";
console.log(user.firstName); // "Ram"
console.log(user.lastName); // "Bahadur"
// get makes fullName look like a property, but it's a function in disguise
// set allows fullName to be assigned like a value, but again — it's a function inside
// syntax:
let obj = {
get propName() {
// runs when obj.propName is accessed
},
set propName(value) {
// runs when obj.propName = value is assigned
}
};
// class
class Circle {
constructor(radius) {
this._radius = radius;
}
get diameter() {
return this._radius * 2;
}
set diameter(value) {
this._radius = value / 2;
}
}
const c = new Circle(10);
console.log(c.diameter); // 20 ✅
c.diameter = 30;
console.log(c); // _radius is now 15 ✅
OOP in JS
Object-Oriented Programming (OOP) is a coding style where you model real-world things using objects.
OOP is based on 4 pillars:
- Encapsulation: Bundle data + behavior in one unit (object/class)
- Abstraction: Hide internal complexity, show only what’s necessary
- Inheritance: One object/class can inherit features from another
- Polymorphism: Same method behaves differently on different objects
JS is prototype-based, not class-based originally — but ES6 introduced class syntax to make it feel more traditional.
// Prototypes and Inheritance
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log("Hi, I'm " + this.name);
};
}
const p1 = new Person("Ram", 20);
p1.greet(); // Hi, I'm Ram
Person.prototype.sayAge = function () {
console.log(this.age);
};
p1.sayAge(); // 20
// The Modern Way — ES6 Classes
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hi, I'm ${this.name}`);
}
}
const p = new Person("Hari", 35);
p.greet(); // Hi, I'm Hari
// Under the hood, ES6 classes uses prototypes, but the syntax is cleaner
Inheritance with extends
you can override methods to create polymorphism
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks`);
}
}
const d = new Dog("Tommy");
d.speak(); // Tommy barks
Super Keyword
Use super() to call the parent class's constructor or methods
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
super.speak(); // Tommy makes a noise
console.log(`${this.name} barks loudly`);
}
}
Encapsulation with # (Private fields)
ES2022 introduced fully private data in js.
class Account {
#balance = 0;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const a = new Account();
a.deposit(100);
console.log(a.getBalance()); // 100
// a.#balance ❌ Error: private field
Static Methods
static methods are class methods.
class MathUtil {
static add(a, b) {
return a + b;
}
}
console.log(MathUtil.add(2, 3)); // 5
Object.create() — Direct Prototypal Inheritance
No classes needed — inheritance via prototype directly
const human = {
sayHi() {
console.log("Hello human");
}
};
const student = Object.create(human);
student.name = "Sita";
student.sayHi(); // Hello human
Getter and Setter
class Product {
#price = 0;
constructor(price) {
this.#price = price; // this will use the setter below!
}
get price() {
return this.#price;
}
set price(val) {
if (typeof val !== "number" || val < 0) {
throw new Error("Invalid price: must be a non-negative number");
}
this.#price = val;
}
}
const acc = new Product();
try {
acc.price = -999; // ❌
} catch (err) {
console.error( 'Error:', err.message );
}
Benefits of Getter & Setter
- Constructor = sets up the initial state
- Getters/Setters = control and manage future access
- Constructor = One-time Setup. That is: Does not protect or validate future changes
- A constructor calls the setter if you write this.name = name (it's smart)
- Private fields (#field) can only be accessed via get/set
Prototype
- Every JavaScript object has a hidden property called [[Prototype]], which is a reference to another object.
- This is how JS does inheritance. No classical classes, no copy-paste of methods — just prototype links.
- __proto__ is how one object inherits from another.
- __proto__: The internal link to the prototype object
- .prototype: Where shared methods go for constructor functions
- Object.create(proto): Create an object with a specific prototype
- instance.__proto__: points to → Constructor.prototype
- Constructor.prototype: contains shared methods like .sayHi()
- Object.getPrototypeOf(): Gets actual prototype
const user = { name: "Bharat" };
console.log(user.__proto__); // 💥 This is the prototype of 'user'
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function () {
console.log(`Hi, I'm ${this.name}`);
};
const p1 = new Person("Ram");
p1.sayHi(); // Hi, I'm Ram ✅
// sayHi is stored in Person.prototype, and every instance of Person shares it
// prototype chain example:
const obj = {};
console.log(obj.__proto__); // Object.prototype
console.log(obj.__proto__.__proto__); // null (end of chain)
Constructor Property
- The constructor property of an object points to the function that created it.
- Class-based syntax automatically sets .constructor correctly. You don't have to manually set it (unless you override prototype manually)
function Person(name) {
this.name = name;
}
const p = new Person("Bharat");
console.log(p.constructor); // 👉 Person
p.__proto__ === Person.prototype // true
Person.prototype.constructor === Person // true
constructor vs prototype
- .prototype: Functions only (Blueprint for instances (shared methods))
- .constructor: Objects & prototypes (Points back to the function that created it)