JavaScript Concepts
1. New features in ES6 version:
1. let and const
let allows re-assignment, const does not.
let count = 10;
count = 20; // ✅ allowed
const pi = 3.14;
// pi = 3.1416 ❌ error
Output: count = 20, pi = 3.14
2. Arrow Functions
const numbers = [1, 2, 3];
const squares = numbers.map(n => n * n);
Squares: 1,4,9
3. Template Literals
let name = 'Arpit';
let msg = `Hello ${name}, Welcome to Angular 17!`;
Message: Hello Arpit, Welcome to Angular 17!
4. Default Parameters
const greet = (name = 'Guest') => `Hello, ${name}`;
greet(); // Hello, Guest
greet('Arpit'); // Hello, Arpit
Output:Hello, Guest, Hello, Arpit
5. Destructuring
const student = { id: 1, username: 'John' };
const { username } = student;
Message: John is learning Angular
6. Spread & Rest
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4]; // Spread
const sum = (...nums) => nums.reduce((a,b) => a+b, 0); // Rest
Spread Result: 1, 2, 3, 4
Rest Sum: 30
2. static Vs dynamic
1. Dynamically Typed (JavaScript)
JavaScript variables do not have fixed types. They can change type at runtime.
let dynamicVar = 42; // number
dynamicVar = "Hello"; // string
dynamicVar = true; // boolean
Output:
dynamicVar = 42 (number) dynamicVar = "Hello" (string) dynamicVar = true (boolean)
2. Statically Typed (TypeScript)
TypeScript variables have a type declared and cannot be assigned a different type.
let staticNum: number = 10;
staticNum = 20; // ✅ allowed
staticNum = "Hello"; // ❌ Error: Type 'string' not assignable to type 'number'
Output:
staticNum = 10 (number)
3. Scope in JavaScript
1. Global Scope
Variables declared outside any function are global and accessible everywhere.
let globalVar = "I am global";
function test() {
console.log(globalVar); // ✅ Accessible
}
test();
console.log(globalVar); // ✅ Accessible
Output:
I am global | I am global
2. Function Scope
Variables declared inside a function are local to that function and not accessible outside.
function test() {
let localVar = "I am local";
console.log(localVar); // ✅ Accessible
}
test();
console.log(localVar); // ❌ ReferenceError
Output:
I am local | ReferenceError: localVar is not accessible outside function
3. Block Scope
let and const are block-scoped, while var is function-scoped.
{
let blockVar = "I am block scoped";
const constBlock = "Me too";
console.log(blockVar); // ✅
}
console.log(blockVar); // ❌ ReferenceError
Output:
I am block scoped | Me too | ReferenceError: blockVar is not accessible outside block
4. Lexical Scope
Functions remember the scope in which they were defined (not where they are called).
function outer() {
let outerVar = "Outer";
function inner() {
console.log(outerVar); // ✅ Accesses parent scope
}
return inner;
}
let myFunc = outer();
myFunc(); // "Outer"
Output:
Outer
5. Scope Chain
JavaScript looks for variables in the current scope, then outer scopes, up to the global scope.
let a = 10;
function outer() {
let b = 20;
function inner() {
let c = 30;
console.log(a, b, c); // ✅ 10, 20, 30
}
inner();
}
outer();
Output:
10, 20, 30
4. Differences between var, let & const
1. var
var is function-scoped, allows redeclaration & reassignment, and is hoisted (initialized with undefined).
function testVar() {
if (true) {
var x = 10;
}
console.log(x); // ✅ 10 (function scoped)
}
testVar();
console.log(y); // undefined (hoisted)
var y = 5;
Output:
Inside function: 10 | Before declaration y: undefined | After assignment y: 5
2. let
let is block-scoped, does not allow redeclaration in the same scope, but reassignment is allowed.
function testLet() {
if (true) {
let x = 20;
console.log(x); // ✅ 20
}
console.log(x); // ❌ ReferenceError (block scoped)
}
let z = 30;
// let z = 40; // ❌ Error (re-declaration not allowed)
Output:
Inside block: 20 | Outside block: ReferenceError (x not accessible) | Initial z: 30 | After reassignment z: 40
3. const
const is block-scoped, cannot be redeclared or reassigned, but object/array contents can still be mutated.
const pi = 3.14;
// pi = 3.1416; // ❌ Error (cannot reassign)
const user = { name: "Arpit" };
user.name = "John"; // ✅ Allowed (object mutation)
Output:
pi: 3.14 | Reassign pi (not allowed) | User after mutation: {"name":"John"}5. Hoisting in JavaScript
1. Function Hoisting
Function declarations are hoisted, so they can be called before they are defined.
test(); // ✅ Works due to hoisting
function test() {
return "I am hoisted!";
}
Output:
I am hoisted!
2. Var Hoisting
var is hoisted but initialized with undefined.
console.log(a); // ✅ undefined (hoisted)
var a = 10;
console.log(a); // 10
Output:
Before declaration: undefined | After initialization: 10
3. Let & Const Hoisting
let and const are hoisted but stay in a "temporal dead zone" until they are initialized. Accessing them before declaration throws a ReferenceError.
try {
console.log(b); // ❌ ReferenceError
let b = 20;
} catch (err) {
console.log(err.message);
}
try {
console.log(c); // ❌ ReferenceError
const c = 30;
} catch (err) {
console.log(err.message);
}
Output:
Let before declaration: Cannot access 'o' before initialization || Const before declaration: Cannot access 'o' before initialization
6. Temporal Dead Zone (TDZ)
1. Temporal Dead Zone
Variables declared with let and const are in a "temporal dead zone" from the start of the block until the declaration is evaluated. Accessing them before declaration throws a ReferenceError.
console.log(x); // ❌ ReferenceError: Cannot access 'x' before initialization
let x = 10;
try {
console.log(y); // ❌ ReferenceError: Cannot access 'y' before initialization
const y = 20;
} catch (err) {
console.log(err.message);
}
Output:
7. Closure in JavaScript
1. Simple Closure
A closure is a function that remembers the scope in which it was created.
function outerFunction() {
let count = 0;
return function innerFunction() {
count++;
return count;
};
}
const counter = outerFunction();
console.log(counter());
console.log(counter());
console.log(counter());
Output:
1 | 2 | 3
2. Closure with Parameters
Closures can also capture arguments passed to the outer function.
function greet(name) {
return function(message) {
return `Hello ${name}, ${message}`;
};
}
const greetArpit = greet('Arpit');
console.log(greetArpit('Welcome!'));
console.log(greetArpit('How are you?'));
Output:
Hello Arpit, Welcome! | Hello Arpit, How are you?
8. Differences between == and ===
== vs ===
== compares **values** after type coercion, while === compares **both value and type**.
let a: any = 5;
let b: any = '5';
console.log(a == b); // true, only value is compared
console.log(a === b); // false, value + type are compared
Output:
true | false
9. NaN in JavaScript
1. NaN (Not-a-Number)
`NaN` represents a value that is not a valid number. It occurs when arithmetic operations fail to produce a numeric result.
let a: any = "Hello";
let b = Number(a); // NaN
console.log(b); // NaN
console.log(isNaN(b)); // true
console.log(Number.isNaN(b)); // true
Output:
b: NaN | isNaN(b): true | Number.isNaN(b): true
2. Operations resulting in NaN
const result = "abc" / 2;
console.log(result); // NaN
Output:
NaN
10. Difference between null and undefined
1. null
null represents an explicitly assigned “no value”.
let a = null;
console.log(a); // null
console.log(typeof a); // "object"
Output:
2. undefined
undefined means a variable has been declared but not assigned a value.
let b;
console.log(b); // undefined
console.log(typeof b); // "undefined"
Output:
11. BOM vs DOM in JavaScript
1. DOM (Document Object Model)
The DOM represents the HTML document structure. It allows JavaScript to manipulate elements, attributes, and content.
console.log(document.title); // Logs the page title
document.body.style.backgroundColor = "#f0f0f0"; // Changes page background
Output:
2. BOM (Browser Object Model)
The BOM provides objects to interact with the browser (window, navigator, screen, location, history).
console.log(window.innerWidth); // Width of the browser window
console.log(navigator.userAgent); // Browser info
Output:
12. Critical Rendering Path (CRP) in JavaScript
1. Build the DOM
The browser parses the HTML and creates the DOM (Document Object Model).
<h1>Hello World</h1>
<p>Welcome to CRP</p>
Example in JS:
console.log(document.body); // Shows the DOM structure
2. Build the CSSOM
The browser parses CSS and creates the CSSOM (CSS Object Model).
p {
color: blue;
font-size: 18px;
}
Effect: Styles are applied when CSSOM is combined with DOM.
3. Render Tree
The DOM and CSSOM are combined to create the Render Tree, which only includes visible elements.
// Example: Hidden elements (display:none) won't appear in Render Tree
4. Layout (Reflow)
The browser calculates size and position of each element.
document.querySelector("p").style.margin = "50px";
// Triggers reflow (layout recalculation)
5. Paint & Composite
The browser fills in pixels (colors, text, borders, images) and renders layers to the screen.
// Example: Change background (repaint)
document.body.style.backgroundColor = "lightyellow";
⚡ JavaScript's Role
- Blocking: Scripts without async or defer pause HTML parsing.
- Modifying: JavaScript changes DOM/CSSOM, which may trigger reflow & repaint.
<script src="app.js"></script>
<script src="app.js" defer></script>
🚀 Optimization Tips
- Use
asyncordeferfor scripts - Inline critical CSS
- Lazy-load non-essential JS/CSS
- Minify and compress resources
13. Basic JavaScript Array Methods
1. forEach()
Executes a provided function once for each array element.
let numbers = [1, 2, 3];
numbers.forEach(n => console.log(n));
Output:
[1,2,3]
2. map()
Creates a new array by applying a function to each element.
let doubled = numbers.map(n => n * 2);
console.log(doubled);
Output:
[2,4,6]
3. filter()
Creates a new array with elements that pass a condition.
let evens = numbers.filter(n => n % 2 === 0);
console.log(evens);
Output:
[2]
4. reduce()
Reduces the array to a single value by applying a function.
let sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum);
Output:
6
5. find()
Returns the first element that satisfies a condition.
let firstEven = numbers.find(n => n % 2 === 0);
console.log(firstEven);
Output:
2
6. includes()
Checks if an array contains a certain element.
let hasTwo = numbers.includes(2);
console.log(hasTwo);
Output:
true
14. Rest Parameter & Spread Operator
1. Rest Parameter (...args)
Allows a function to accept an indefinite number of arguments as an array.
function sumAll(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sumAll(1, 2, 3, 4, 5));
Output:
15
2. Spread Operator (...)
Expands elements of an array (or object properties) into individual elements.
let nums = [1, 2, 3];
let moreNums = [...nums, 4, 5];
console.log(moreNums);
let obj1 = { a: 1, b: 2 };
let obj2 = { ...obj1, c: 3 };
console.log(obj2);
Output:
[1,2,3,4,5]
{"a":1,"b":2,"c":3}15. The "this" Keyword
1. The "this" Keyword
this refers to the object that is currently executing the function. Its value depends on how the function is called.
let person = {
name: "Alice",
greet: function() {
return "Hello, my name is " + this.name;
}
};
console.log(person.greet());
// Arrow function (lexical 'this')
let person2 = {
name: "Bob",
greet: () => {
return "Hello, my name is " + this.name;
}
};
console.log(person2.greet());
Output:
Hello, my name is Alice Hello, my name is Default Name
16. call(), apply(), and bind()
1. call()
Executes a function with a given this and arguments (comma-separated).
2. apply()
Similar to call(), but arguments are passed as an array.
3. bind()
Returns a new function with this bound, does not execute immediately.
17. JavaScript Single-Threaded & Event Loop
1. Is JavaScript Single-Threaded?
Yes, JavaScript runs on a single thread. But it can handle asynchronous operations using the event loop.
2. Event Loop Example
Shows how asynchronous callbacks are executed after the call stack is empty.
console.log("Start");
setTimeout(() => {
console.log("Timeout callback");
}, 0);
console.log("End");
Output:
Start End Timeout callback
18. Callbacks, Promises, and Async/Await
1. Callback
A callback is a function passed into another function to be executed later.
function greet(name, callback) {
callback('Hello ' + name);
}
greet('Arpit', function(message) {
console.log(message);
});
Output:
Hello Arpit
2. Promise
A Promise represents a value that may be available now, or in the future, or never.
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('Data received!'), 1000);
});
}
fetchData().then(console.log);
Output:
Data received!
3. Async/Await
Async/Await is syntactic sugar over Promises, allowing asynchronous code to be written like synchronous code.
function fetchData() {
return new Promise(resolve => setTimeout(() => resolve('Data received!'), 1000));
}
async function getData() {
const result = await fetchData();
console.log(result);
}
getData();
Output:
Data received!
19. Callback Hell
1. What is Callback Hell?
Callback Hell happens when multiple asynchronous operations are nested, making the code unreadable and difficult to maintain.
setTimeout(() => {
console.log("Step 1");
setTimeout(() => {
console.log("Step 2");
setTimeout(() => {
console.log("Step 3");
setTimeout(() => {
console.log("Step 4");
}, 1000);
}, 1000);
}, 1000);
}, 1000);
Output:
Step 1 Step 2 Step 3 Step 4
20. Observables
1. What is an Observable?
An Observable is a data stream that can emit multiple values over time. Observables are lazy and require subscription to start emitting values.
import { Observable } from 'rxjs';
const myObservable = new Observable(subscriber => {
subscriber.next('First value');
subscriber.next('Second value');
setTimeout(() => {
subscriber.next('Third value (after 1 sec)');
subscriber.complete();
}, 1000);
});
myObservable.subscribe({
next: value => console.log(value),
complete: () => console.log('Observable completed')
});
Output:
First value Second value Third value (after 1 sec) Observable completed
21. Promises vs Observables
Differences Between Promises and Observables
- Promise emits a single value; Observable can emit multiple values over time.
- Promise is eager (executes immediately); Observable is lazy (executes on subscription).
- Observable supports operators like map, filter, debounce, etc.
- Observable can be canceled/unsubscribed; Promise cannot be canceled once started.
- Promise uses `.then()`/`.catch()`; Observable uses `.subscribe()`.
/* Promise Example */
const myPromise = new Promise((resolve) => {
resolve('Promise resolved');
});
myPromise.then(console.log);
/* Observable Example */
const myObservable = new Observable(subscriber => {
subscriber.next('Observable first value');
subscriber.next('Observable second value');
subscriber.complete();
});
myObservable.subscribe({
next: console.log,
complete: () => console.log('Observable completed')
});
Output:
Observable first value Observable second value Observable completed Promise resolved
22. setTimeout vs setImmediate vs process.nextTick
Difference Between setTimeout, setImmediate, and process.nextTick
- setTimeout(callback, 0): Executes after 0ms in the timers phase.
- setImmediate(callback): Executes in the check phase, immediately after I/O events.
- process.nextTick(callback): Executes before the next event loop tick, before other asynchronous callbacks.
console.log('Start');
setTimeout(() => console.log('setTimeout 0ms'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('process.nextTick'));
console.log('End');
Output:
Start End process.nextTick setImmediate setTimeout 0ms
23. Microtasks in JavaScript
What is a Microtask?
Microtasks are asynchronous tasks that run immediately after the current synchronous code, but before the next macrotask (like setTimeout). They have higher priority than macrotasks.
console.log("Start");
setTimeout(() => console.log("setTimeout (Macrotask)"), 0);
Promise.resolve().then(() => console.log("Promise.then (Microtask)"));
queueMicrotask(() => console.log("queueMicrotask (Microtask)"));
console.log("End");
Output:
Start\nEnd\nPromise.then (Microtask)\nqueueMicrotask (Microtask)\nsetTimeout (Macrotask)
24. Pure Functions in JavaScript
What is a Pure Function?
A Pure Function always produces the same output for the same input and does not cause any side effects. Functions that modify external state or depend on it are impure.
// Pure Function Example
function multiply(a: number, b: number): number {
return a * b;
}
console.log(multiply(2, 3));
console.log(multiply(2, 3));
// Impure Function Example
let counter = 0;
function increment(): number {
return ++counter;
}
console.log(increment());
console.log(increment());
Output:
multiply(2, 3) → 6 multiply(2, 3) → 6 increment() → 1 increment() → 2
25. Error Object and Types in JavaScript
What is an Error Object?
An Error object provides information about an error. It has two main properties: name (the type of error) and message (description).
Common Error Types:
- Error – generic error
- ReferenceError – variable not found
- TypeError – invalid operation on a value
- SyntaxError – incorrect code syntax
- RangeError – invalid range (e.g., negative array length)
- EvalError – error in
eval()usage - URIError – invalid
encodeURI/decodeURIusage
// Different JavaScript Error Objects
try {
throw new Error("Generic Error Example");
} catch (e) {
console.log(e.name, "-", e.message);
}
try {
// ReferenceError
console.log(undeclaredVar);
} catch (e) {
console.log(e.name, "-", e.message);
}
try {
// TypeError
null.f();
} catch (e) {
console.log(e.name, "-", e.message);
}
try {
// RangeError
let arr = new Array(-1);
} catch (e) {
console.log(e.name, "-", e.message);
}
try {
// URIError
decodeURIComponent("%");
} catch (e) {
console.log(e.name, "-", e.message);
}
Output:
Error - Generic Error Example ReferenceError - window is not defined TypeError - Cannot read properties of null (reading 'f') RangeError - Invalid array length URIError - URI malformed
26. Error Handling Statements in JavaScript
What are the various statements in error handling?
- try → Defines code to test for errors.
- catch → Handles the error if it occurs.
- finally → Always runs, whether error occurs or not.
- throw → Used to create custom errors.
// Error Handling Statements Example
try {
console.log("Before error");
// Throwing a custom error
throw new Error("Something went wrong!");
console.log("This will not run");
} catch (e) {
console.log("Caught an error:", e.message);
} finally {
console.log("Finally block always executes");
}
Output:
Before error Caught an error: Something went wrong! Finally block always executes
27. Strict Mode in JavaScript
What do you mean by strict mode in JavaScript and its characteristics?
- Variables must be declared before use.
- Duplicate function parameters are not allowed.
thisinside a function isundefined(not global).- Reserved keywords cannot be used as identifiers.
- Deleting variables, functions, or function parameters is not allowed.
"use strict";
// Example 1: Using undeclared variable
try {
num = 10; // ❌ Error in strict mode
console.log(num);
} catch (error) {
console.log("Error:", error.message);
}
// Example 2: 'this' inside function
function checkThis() {
console.log("'this' in strict mode:", this);
}
checkThis();
// Example 3: Deleting variable
try {
let x = 100;
// delete x; // ❌ Not allowed
} catch (error) {
console.log("Error:", error.message);
}
Output:
Error: num is not defined 'this' in strict mode: undefined Error: Delete of an unqualified identifier in strict mode.
28. Cookie vs Local Storage vs Session Storage
What are the differences between Cookie, Local Storage, and Session Storage?
- Cookie → Small data (4KB), sent with every HTTP request, can have expiry.
- Local Storage → Stores large data (~5–10MB), persists even after browser close, not sent to server.
- Session Storage → Similar to Local Storage but data clears when browser tab is closed.
// Cookie Example
document.cookie = "username=John; expires=Fri, 31 Dec 2025 23:59:59 GMT";
console.log("Cookies:", document.cookie);
// Local Storage Example
localStorage.setItem("theme", "dark");
console.log("Local Storage:", localStorage.getItem("theme"));
// Session Storage Example
sessionStorage.setItem("token", "12345");
console.log("Session Storage:", sessionStorage.getItem("token"));Output:
Cookies: username=John Local Storage: dark Session Storage: 12345
29. Prototype Chaining in JavaScript
What is Prototype Chaining?
Prototype chaining is the mechanism by which objects in JavaScript inherit properties and methods from other objects through the __proto__ or prototype chain.
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
return "Hello, my name is " + this.name;
};
function Student(name, grade) {
Person.call(this, name); // inherit properties
this.grade = grade;
}
// Inherit methods via prototype chaining
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.getGrade = function() {
return this.name + " is in grade " + this.grade;
};
const student1 = new Student("Alice", 10);
console.log(student1.sayHello());
console.log(student1.getGrade());Output:
Hello, my name is Alice Alice is in grade 10
30. Generators in JavaScript
What are Generators and their types?
Generators are special functions in JavaScript that can **pause execution** and **resume later**. They are defined using the function* syntax and use yield to produce values.
Types of Generators:
- Simple Generator – produces sequence of values on demand.
- Infinite Generator – can produce values indefinitely until stopped.
- Delegated Generator – uses
yield*to delegate to another generator.
// Simple Generator
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// Infinite Generator
function* infiniteGenerator() {
let i = 0;
while(true) {
yield i++;
}
}
const infGen = infiniteGenerator();
console.log(infGen.next().value); // 0
console.log(infGen.next().value); // 1
// Delegated Generator
function* generatorA() {
yield 'A1';
yield 'A2';
}
function* generatorB() {
yield* generatorA(); // delegate
yield 'B1';
}
const genB = generatorB();
console.log(genB.next().value); // A1
console.log(genB.next().value); // A2
console.log(genB.next().value); // B1Output:
1 2 3 0 1 A1 A2 B1
31. Debouncing vs Throttling in JavaScript
What is the difference between Debouncing and Throttling?
- Debouncing → Ensures a function is called **after a certain delay** only once, after the last event. Useful for input validation, search suggestions.
- Throttling → Ensures a function is called **at most once every interval**. Useful for scroll, resize events.
// Debounce Function
function debounce(func: Function, delay: number) {
let timer: any;
return function(...args: any[]) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
const debouncedFunc = debounce(() => console.log("Debounced!"), 1000);
debouncedFunc();
debouncedFunc();
debouncedFunc(); // Only this call will execute after 1 sec
// Throttle Function
function throttle(func: Function, limit: number) {
let lastFunc: any;
let lastRan: number;
return function(...args: any[]) {
if (!lastRan) {
func.apply(this, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if ((Date.now() - lastRan) >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
const throttledFunc = throttle(() => console.log("Throttled!"), 2000);
throttledFunc();
throttledFunc();
throttledFunc(); // Executes immediately and then once every 2 secOutput:
Debounced! (after 1 sec) Throttled! (immediately) Throttled! (after 2 sec if called again)
