Top Interview Questions
ES6, also known as ECMAScript 2015, is a major update to JavaScript that significantly improved the language by adding new features, syntax enhancements, and better support for large-scale application development. Before ES6, JavaScript (ES5) lacked many modern programming constructs, making code harder to maintain, scale, and read. ES6 addressed these limitations by introducing cleaner syntax, modularity, better variable scoping, and improved asynchronous programming capabilities.
ES6 was released in 2015 by ECMA International, the organization responsible for standardizing JavaScript. Since then, ES6 has become the foundation for modern JavaScript development and is widely supported by all major browsers and JavaScript runtimes like Node.js.
One of the most important additions in ES6 is the introduction of let and const for variable declaration.
let allows block-scoped variables, unlike var which is function-scoped.
const is used to declare constants whose values cannot be reassigned.
Example:
let count = 10;
const PI = 3.14;
Block scoping helps prevent bugs caused by variable hoisting and accidental re-declaration.
Arrow functions provide a shorter syntax for writing functions and automatically bind the this keyword to the surrounding context.
Example:
const add = (a, b) => a + b;
Benefits:
Cleaner syntax
No need for function keyword
Lexical this binding, especially useful in callbacks
Template literals allow embedded expressions and multi-line strings using backticks (`).
Example:
let name = "Yash";
let message = `Hello ${name}, welcome to ES6!`;
Advantages:
String interpolation
Multi-line strings
Improved readability
Destructuring allows extracting values from arrays or objects into variables easily.
Example:
const user = { name: "Amit", age: 25 };
const { name, age } = user;
This feature reduces repetitive code and improves clarity.
Functions can now have default parameter values, avoiding manual checks for undefined.
Example:
function greet(name = "Guest") {
return `Hello, ${name}`;
}
This leads to more robust and readable function definitions.
The rest operator (...) collects multiple values into an array, while the spread operator expands elements.
Example:
function sum(...numbers) {
return numbers.reduce((a, b) => a + b);
}
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
These operators simplify array and function argument handling.
ES6 introduced a class syntax that provides a cleaner way to implement object-oriented programming.
Example:
class Person {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}`;
}
}
Although JavaScript is still prototype-based, classes make OOP concepts more accessible and readable.
ES6 added native support for modules, allowing code to be split into reusable files.
Example:
// math.js
export function add(a, b) {
return a + b;
}
// main.js
import { add } from './math.js';
Modules improve:
Code organization
Reusability
Maintainability
Promises simplify asynchronous programming and reduce callback hell.
Example:
const fetchData = () => {
return new Promise((resolve, reject) => {
resolve("Data received");
});
};
fetchData().then(data => console.log(data));
Promises make async code easier to understand and manage.
ES6 allows shorthand property names and method definitions.
Example:
let name = "Raj";
let user = {
name,
greet() {
return "Hello!";
}
};
This reduces boilerplate code and improves readability.
The for...of loop provides a simpler way to iterate over iterable objects like arrays, strings, and maps.
Example:
for (let value of [1, 2, 3]) {
console.log(value);
}
This loop is cleaner than traditional for loops for many use cases.
ES6 introduced new data structures:
Map: Key-value pairs with any type of key
Set: Collection of unique values
Example:
let set = new Set([1, 2, 2, 3]);
let map = new Map();
map.set("name", "Ankit");
These structures provide better performance and flexibility compared to objects and arrays.
Cleaner and more readable syntax
Better variable scoping and memory management
Improved modular programming
Easier asynchronous handling
Better support for large applications
Encourages modern JavaScript best practices
Answer:
ES6 stands for ECMAScript 6, also known as ECMAScript 2015. It is a major update to JavaScript that introduced new features to make the language more powerful, readable, and easier to write.
Why ES6 is important:
Cleaner syntax
Better handling of variables
Support for object-oriented programming
Modern features like arrow functions, promises, and modules
var, let, and constvarFunction scoped
Can be re-declared and re-assigned
Hoisted with undefined
letBlock scoped
Can be re-assigned but not re-declared
Safer than var
constBlock scoped
Cannot be re-assigned or re-declared
Used for constant values
Example:
var a = 10;
let b = 20;
const c = 30;
Answer:
A variable declared inside { } using let or const is only accessible within that block.
Example:
{
let x = 5;
}
console.log(x); // Error
Answer:
Arrow functions provide a shorter syntax for writing functions and do not have their own this.
Syntax:
const add = (a, b) => a + b;
Advantages:
Shorter code
No need to write return for single expressions
No own this
| Normal Function | Arrow Function |
|---|---|
Has its own this |
Does not have this |
Uses function keyword |
Uses => |
| Can be used as constructor | Cannot be constructor |
Answer:
Template literals allow string interpolation using backticks ( ).
Example:
let name = "Yash";
console.log(`Hello ${name}`);
Benefits:
Easy variable insertion
Multi-line strings
Answer:
Destructuring allows unpacking values from arrays or objects into variables.
let [a, b] = [10, 20];
let {name, age} = {name: "Rahul", age: 25};
Answer:
Default parameters allow function parameters to have default values.
Example:
function greet(name = "Guest") {
return `Hello ${name}`;
}
...)?Answer:
The spread operator expands elements of arrays or objects.
Example:
let arr1 = [1, 2];
let arr2 = [...arr1, 3, 4];
Answer:
Rest parameter collects multiple arguments into an array.
Example:
function sum(...numbers) {
return numbers.reduce((a, b) => a + b);
}
| Spread | Rest |
|---|---|
| Expands values | Collects values |
| Used in function calls | Used in parameters |
Answer:
Modules allow splitting code into multiple files.
Export:
export const name = "ES6";
Import:
import { name } from "./file.js";
Answer:
Classes are templates for creating objects.
Example:
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log("Hello " + this.name);
}
}
Answer:
A constructor is a special method that runs automatically when an object is created from a class.
Answer:
Inheritance allows one class to use properties of another class using extends.
Example:
class Student extends Person {
constructor(name, roll) {
super(name);
this.roll = roll;
}
}
super Keyword?Answer:
super is used to call the parent class constructor or methods.
Answer:
Promises handle asynchronous operations.
States:
Pending
Fulfilled
Rejected
Example:
let promise = new Promise((resolve, reject) => {
resolve("Success");
});
async and await?Answer:
They make asynchronous code look synchronous.
Example:
async function fetchData() {
let result = await fetch(url);
}
for...of Loop?Answer:
Used to iterate over arrays and strings.
for (let val of [1,2,3]) {
console.log(val);
}
Map?Answer:
Map stores key-value pairs and allows any data type as key.
let map = new Map();
map.set("name", "ES6");
Set?Answer:
Set stores unique values only.
let set = new Set([1,2,2,3]);
Answer:
var is hoisted
let and const are hoisted but not initialized (Temporal Dead Zone)
Answer:
The time between variable declaration and initialization where it cannot be accessed.
Answer:
A primitive data type used to create unique identifiers.
let id = Symbol("id");
Answer:
Cleaner syntax
Less boilerplate code
Better scope management
Supports modern development
Object.assign() in ES6?Answer:
Object.assign() is used to copy properties from one or more source objects to a target object.
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let result = Object.assign({}, obj1, obj2);
console.log(result); // { a:1, b:2 }
Cloning objects
Merging multiple objects
Answer:
ES6 provides shorter syntax when creating objects.
let name = "Yash";
let age = 25;
let user = { name, age };
Less code
Better readability
Object.freeze()?Answer:
Object.freeze() prevents modification of an object.
const user = { name: "Rahul" };
Object.freeze(user);
user.name = "Amit"; // Not allowed
Object.seal()?Answer:
Object.seal() allows modifying existing properties but prevents adding or deleting properties.
freeze and seal| Feature | freeze | seal |
|---|---|---|
| Modify value | β | β |
| Add property | β | β |
| Delete property | β | β |
find() method in ES6?Answer:
find() returns the first element that satisfies a condition.
let nums = [10, 20, 30];
let result = nums.find(n => n > 15);
console.log(result); // 20
findIndex()?Answer:
Returns the index of the first matching element.
nums.findIndex(n => n > 15); // 1
includes() method?Answer:
Checks whether an array or string contains a value.
[1,2,3].includes(2); // true
Array.from()?Answer:
Converts array-like or iterable objects into arrays.
Array.from("HELLO"); // ['H','E','L','L','O']
Array.of()?Answer:
Creates an array from arguments.
Array.of(1,2,3); // [1,2,3]
Array.from() and Array.of()| Array.from | Array.of |
|---|---|
| Converts iterable | Creates array |
| Accepts mapping function | Simple creation |
this behavior in Arrow Functions?Answer:
Arrow functions do not have their own this. They inherit this from the parent scope.
function test() {
this.name = "JS";
setTimeout(() => {
console.log(this.name);
}, 1000);
}
for...in vs for...of?| for...in | for...of |
|---|---|
| Iterates keys | Iterates values |
| Used for objects | Used for arrays |
Answer:
Generators are functions that can pause and resume execution.
function* gen() {
yield 1;
yield 2;
}
yield keyword?Answer:
yield pauses a generator function and returns a value.
Answer:
WeakMap stores objects as keys only and allows garbage collection.
| Map | WeakMap |
|---|---|
| Any key type | Only objects |
| Iterable | Not iterable |
Answer:
Stores weakly referenced objects only.
Answer:
An optimization where recursive calls reuse stack frames (supported in strict mode).
Number.isNaN()?Answer:
Checks if the value is truly NaN.
Number.isNaN("abc"); // false
Number.isInteger()?Answer:
Checks if value is an integer.
Number.isInteger(10); // true
Math.trunc()?Answer:
Removes decimal part.
Math.trunc(4.9); // 4
Math.sign()?Answer:
Returns:
1 for positive
-1 for negative
0 for zero
String.startsWith()?
"Hello".startsWith("He"); // true
String.endsWith()?
"Hello".endsWith("lo"); // true
String.repeat()?
"Hi".repeat(3); // HiHiHi
Object.is()?Answer:
Compares two values strictly (better than === in some cases).
Proxy in ES6?Answer:
Proxy allows custom behavior for object operations.
let p = new Proxy({}, {
get(target, prop) {
return prop in target ? target[prop] : "Not Found";
}
});
Reflect?Answer:
Provides methods for object operations (used with Proxy).
| Default Export | Named Export |
|---|---|
| One per file | Multiple allowed |
| No braces | Uses braces |
Promise.all()?Answer:
Executes multiple promises in parallel.
Promise.race()?Answer:
Returns first resolved or rejected promise.
Promise.catch()?Answer:
Handles errors in promises.
finally() in Promise?Answer:
Executes code regardless of success or failure.
Answer:
ES6 introduces features that enable modular, scalable, and maintainable code.
Key architectural improvements:
Modules (import/export)
Block scoping (let, const)
Classes & inheritance
Promises & async flow
Immutable patterns (spread, destructuring)
π ES6 helps move JS from script-based programming to application-level development.
Answer:
TDZ is the time between entering scope and variable declaration where let and const cannot be accessed.
console.log(a); // ReferenceError
let a = 10;
Why TDZ exists:
Prevents usage of variables before declaration
Avoids bugs caused by hoisting
π TDZ forces clean coding discipline, unlike var.
this behave differently in arrow functions?Answer:
Arrow functions lexically bind this from the parent scope.
class Counter {
constructor() {
this.count = 0;
}
start() {
setInterval(() => {
this.count++;
}, 1000);
}
}
β No need for bind(this)
β Avoids common bugs in callbacks
const be preferred over let?Answer:
const ensures immutability of reference, not value.
const user = { name: "A" };
user.name = "B"; // Allowed
Benefits:
Prevents accidental reassignment
Improves code readability
Helps reasoning about state
π Industry standard: use const by default, let only when needed
const obj2 = { ...obj1 };
Problem:
Nested objects still share reference
const deep = JSON.parse(JSON.stringify(obj));
OR using libraries (recommended for production):
lodash cloneDeep
Answer:
Destructuring is heavily used in:
API responses
Function parameters
Redux / Angular / React props
function getUser({ name, role }) {
console.log(name, role);
}
β Cleaner code
β Avoids repetitive object access
| ES6 Modules | CommonJS |
|---|---|
import/export |
require/module.exports |
| Static | Dynamic |
| Tree-shakable | Not tree-shakable |
| Browser supported | Node.js |
π ES6 modules enable better bundling and performance
Answer:
Tree shaking removes unused code during build time.
import { add } from './math';
Only add is bundled, unused exports removed.
β Smaller bundle size
β Faster load time
Answer:
A Promise is an object that represents future completion.
States:
Pending
Fulfilled
Rejected
new Promise((resolve, reject) => {
resolve(data);
});
Promises use microtask queue, which executes before normal callbacks.
Promise.all, allSettled, race, any| Method | Behavior |
|---|---|
| all | Fails if any fails |
| allSettled | Waits for all |
| race | First settled |
| any | First success |
Answer:
Cleaner syntax
Better error handling
Easier debugging
Synchronous-like flow
try {
const data = await fetchData();
} catch (err) {
console.log(err);
}
Answer:
Order of execution:
Call stack
Microtask queue (Promises)
Macrotask queue (setTimeout)
setTimeout(() => console.log("timeout"));
Promise.resolve().then(() => console.log("promise"));
Output:
promise
timeout
const updated = { ...user, role: "admin" };
function log(...args) {
console.log(args);
}
π Used heavily in reducers, APIs, and reusable utilities.
Map Advantages:
Any key type
Maintains order
Faster iteration
Use Map when:
Dynamic keys
Frequent add/remove
Performance matters
Answer:
Used to remove duplicates.
const uniqueUsers = [...new Set(users)];
Common use:
Tags
Permissions
Unique IDs
Answer:
Proxy intercepts object operations.
const user = new Proxy({}, {
get(target, prop) {
if (!(prop in target)) {
throw new Error("Invalid property");
}
return target[prop];
}
});
Use cases:
Validation
Logging
Security
Reactive frameworks (Vue)
Answer:
Reflect provides standard methods for object operations.
Reflect.get(obj, prop);
Benefits:
Cleaner error handling
Works well with Proxy
Answer:
Symbols create hidden, unique keys.
const ID = Symbol("id");
user[ID] = 101;
Used in:
Libraries
Framework internals
Preventing key collision
Tools:
Spread operator
Object.freeze
Destructuring
const newState = { ...state, count: state.count + 1 };
π Crucial for React, Redux, Angular signals.
Answer:
ES6 solves major ES5 issues like:
Global scope pollution
Callback hell
Poor modularity
Hard-to-maintain prototype syntax
How ES6 fixes this:
let / const → block scoping
Promises / async-await → clean async code
Modules → structured architecture
Classes → readable OOP
π ES6 enables enterprise-scale JavaScript applications.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
Output:
3 3 3
Fix using ES6:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
Output:
0 1 2
π let creates a new scope per iteration.
const work internally?Answer:
const prevents reassignment, not mutation.
const obj = { a: 1 };
obj.a = 2; // allowed
obj = {}; // β error
π Reference is constant, value may change.
console.log(x); // ReferenceError
let x = 10;
Why TDZ exists:
Prevents accidental usage before declaration
Forces predictable execution
π TDZ is a design decision, not a bug.
function Timer() {
this.time = 0;
setInterval(() => {
this.time++;
}, 1000);
}
β Arrow function inherits this
β Normal function would require .bind(this)
Answer:
Avoid arrow functions:
As object methods
As constructors
When dynamic this is required
const obj = {
show: () => console.log(this)
};
π this will be incorrect.
function login({ email, password }) {
// use email & password
}
Benefits:
Clean code
Avoids repetitive access
Safer API contracts
Used heavily in:
React props
API handlers
Redux reducers
function calc(a, b = a * 2) {
return b;
}
calc(5); // 10
π Default parameters are evaluated at runtime, not at definition.
const newState = {
...state,
count: state.count + 1
};
Why important:
Immutability
Change detection
Predictable state
Used in React, Angular, Redux.
function sum(...nums) {
return nums.reduce((a, b) => a + b);
}
Advantages over arguments:
Real array
Works with arrow functions
Cleaner syntax
const a = { x: { y: 1 } };
const b = { ...a };
b.x.y = 2;
Result:
Both a and b change.
π Spread creates shallow copy only.
Answer:
Static imports
Tree-shaking
Smaller bundles
Better dependency analysis
import { add } from './math';
Unused code is removed at build time.
| Default Export | Named Export |
|---|---|
| One per file | Multiple |
| No braces | Uses braces |
| Any name | Exact name |
β Wrong:
fetchData().then(() => {
fetchMore();
});
β Correct:
fetchData().then(() => fetchMore());
π Always return promises in .then().
setTimeout(() => console.log(1));
Promise.resolve().then(() => console.log(2));
console.log(3);
Output:
3
2
1
π Promises execute before timers.
Answer:
Linear flow
Stack traces readable
Easier error handling
try {
await apiCall();
} catch (e) {
handleError(e);
}
Use Map when:
Keys are dynamic
Frequent add/remove
Order matters
const cache = new Map();
cache.set(userId, data);
const uniqueIds = [...new Set(ids)];
Used in:
Permission systems
Deduplication
Feature flags
Answer:
Used for private data storage.
const privateData = new WeakMap();
Garbage collected automatically → prevents memory leaks.
Answer:
Even experienced developers often make these ES6 mistakes:
Overusing arrow functions where this is required
Mutating objects while assuming immutability
Forgetting to return promises in .then()
Using spread operator inside loops (performance issue)
Misunderstanding shallow vs deep copy
π Interviewers look for awareness of trade-offs, not just syntax.
const obj2 = { ...obj1 };
Only first level copied
Nested references remain same
const deep = JSON.parse(JSON.stringify(obj));
Real-world impact:
In state management (React/Redux), shallow copy is enough only if structure is flat.
Answer:
Immutability helps:
Detect changes easily
Prevent side effects
Improve performance via reference checks
const newState = { ...state, count: state.count + 1 };
Used heavily in:
React
Angular (OnPush)
Redux / NgRx
Answer:
ES6 imports are resolved at compile time, not runtime.
import { sum } from './math';
Benefits:
Tree-shaking
Early error detection
Better bundling
Answer:
CommonJS uses dynamic require():
const lib = require('./lib');
Bundlers can’t know what’s used → all code included.
Answer:
Promise has:
State
Result
Callbacks queue
Promises run callbacks in microtask queue, which has higher priority than macrotasks.
.then() and await| .then() | await |
|---|---|
| Callback-based | Synchronous style |
| Harder to debug | Cleaner stack trace |
| Nested possible | Linear flow |
Promise.resolve()
.then(() => {
throw new Error("Failed");
})
.then(() => {})
.catch(err => console.log(err.message));
Errors propagate until caught.
Promise.allSettled()?Answer:
When partial success is acceptable.
Example:
Dashboard widgets loading
Multiple API calls where some may fail
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
Output:
1
4
3
2
Answer:
Continuous promise resolution can delay timers.
function loop() {
Promise.resolve().then(loop);
}
π Timers may never execute.
Map vs Object in caching systemsAnswer:
Map is preferred because:
Keys can be objects
Maintains insertion order
Faster iteration
const cache = new Map();
cache.set(user, data);
WeakMap prevent memory leaks?Answer:
WeakMap keys are weakly referenced.
When key object is garbage collected, value is removed automatically.
Used in:
DOM metadata
Private properties
const uniqueUsers = [...new Set(users)];
Used in:
Permissions
Tag systems
User lists
Symbol with collision prevention example
const id = Symbol("id");
obj[id] = 1;
Symbols avoid conflicts with user-defined keys.
Symbol.iterator and custom iteration
const collection = {
*[Symbol.iterator]() {
yield 1;
yield 2;
}
};
Allows for...of iteration.
| Generator | Async |
|---|---|
| Pausable | Promise-based |
| Manual iteration | Automatic |
| yield | await |
Async functions internally use promises.
Object.freeze() limitationsAnswer:
Freeze is shallow.
Object.freeze(obj);
obj.inner.value = 10; // Allowed
For deep freeze, recursion is needed.
Use const by default
Avoid deep cloning
Prefer Map/Set
Keep async code flat
Avoid shared mutable state