KEMBAR78
Technical Interview Questions and Answers | PDF | Pointer (Computer Programming) | Class (Computer Programming)
0% found this document useful (0 votes)
12 views38 pages

Technical Interview Questions and Answers

This document provides a comprehensive set of technical interview questions and solutions for junior full-stack developers, focusing on essential programming concepts, particularly in C. It covers topics such as variable scope, memory management, pointers, and the differences between macros and functions, along with practical examples and explanations. The content aims to assess foundational knowledge and enhance understanding of core programming principles relevant to software development.

Uploaded by

2101020064
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views38 pages

Technical Interview Questions and Answers

This document provides a comprehensive set of technical interview questions and solutions for junior full-stack developers, focusing on essential programming concepts, particularly in C. It covers topics such as variable scope, memory management, pointers, and the differences between macros and functions, along with practical examples and explanations. The content aims to assess foundational knowledge and enhance understanding of core programming principles relevant to software development.

Uploaded by

2101020064
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

Technical Interview Questions and

Solutions for a Junior Full-Stack


Developer
This report provides a comprehensive set of technical interview questions and detailed solutions
tailored for a junior full-stack developer, drawing upon the skills and experience outlined in the
provided resume. The questions are designed to assess foundational knowledge across various
programming languages, backend and frontend technologies, databases, API integrations, and
developer tools. Each answer offers in-depth explanations, practical examples, and highlights
the significance of the concepts within the broader context of software development.

A. C Programming Fundamentals
C programming serves as a fundamental building block in computer science, and proficiency in
its core concepts demonstrates a strong grasp of low-level system interactions and memory
management. The questions in this section delve into the foundational aspects of C, which are
crucial for understanding how programs operate closer to the hardware.

A.1. Why is C called a mid-level programming language?


C is categorized as a mid-level programming language because it uniquely combines features
of both low-level (assembly-level) and high-level languages. This dual nature allows developers
significant flexibility. On one hand, C provides direct memory manipulation capabilities, similar to
assembly languages, enabling tasks such as writing operating systems or embedded systems
where fine-grained control over hardware is necessary. On the other hand, it offers high-level
constructs like functions, loops, and conditional statements, making it more human-readable
and portable than assembly. This versatility allows for the development of complex applications,
such as menu-driven consumer billing systems, alongside system-level programming. The
language's ability to bridge these two levels is a defining characteristic that makes it powerful
and widely applicable.

A.2. What are the basic data types supported in C Programming


Language?
C supports several fundamental data types that serve as the basic units for storing different
kinds of values. These include int for integers (whole numbers), char for single characters, float
for single-precision floating-point numbers, and double for double-precision floating-point
numbers. These types are essential for declaring variables and defining the kind of data they
can hold, forming the bedrock upon which more complex data structures are built.

A.3. What are tokens in C?


Tokens are the smallest individual units in a C program that hold meaning for the compiler. They
are the fundamental building blocks from which a C program is constructed. In C, there are six
main types of tokens:
●​ Keywords: These are predefined or reserved words in the C language, such as int, while,
if, for, return, etc., each having a specific meaning to the compiler.
●​ Identifiers: These are user-defined names given to program elements like variables,
functions, arrays, and structures. They consist of letters, digits, or underscores, starting
with a letter or an underscore.
●​ Constants: These represent fixed values that do not change during the execution of the
program. Examples include integer constants (e.g., 10, 100), floating-point constants
(e.g., 3.14, 0.001), character constants (e.g., 'A', 'z'), and string literals (e.g., "Hello
World").
●​ Operators: These are symbols that perform specific operations on operands. Common
operators include arithmetic (+, -, *, /), relational (==, !=, <, >), logical (&&, ||, !), and
assignment (=).
●​ Special Characters: These are symbols that have specific meanings and are used for
various purposes, such as curly braces {} for blocks, parentheses () for function calls, and
semicolons ; for statement termination.
●​ Strings: These are sequences of characters enclosed in double quotes, treated as a
single unit (e.g., "Programming"). Understanding tokens is crucial for parsing and
compiling C code.

A.4. Explain the concept of variable scope in C.


Variable scope in C defines the region of a program where a declared variable is accessible.
The location where a variable is declared determines its visibility and lifetime. There are three
primary types of variable scope in C:
●​ Local Variables: These variables are declared inside a function or a specific block of
code. Their accessibility is strictly limited to that function or block. They are created when
the function or block is entered and are automatically destroyed when execution exits that
scope. If not explicitly initialized, local variables typically contain arbitrary "garbage"
values.
●​ Global Variables: Declared outside of all functions, typically at the beginning of the
program file. Global variables are accessible from any part of the program, throughout all
functions. They exist for the entire duration of the program's execution and are
automatically initialized to zero if no explicit value is provided.
●​ Formal Parameters: These are variables declared in the parameter list of a function.
Their scope is limited to the function in which they are declared, serving as placeholders
for the arguments passed during a function call. Proper management of variable scope
helps prevent unintended side effects and promotes modular code.

A.5. What are preprocessor directives in C?


Preprocessor directives are special instructions that the C preprocessor processes before the
actual compilation phase begins. These directives start with a hash symbol (#) and are not
executable statements themselves. Their primary role is to manipulate the source code before it
is passed to the compiler. Common preprocessor directives include:
●​ #include: Used to include the content of a specified header file into the current source file.
This is essential for bringing in declarations for standard library functions (e.g., stdio.h for
printf and scanf) or user-defined functions.
●​ #define: Used to define macros, which are symbolic constants or simple function-like
substitutions. When the preprocessor encounters a macro, it replaces all occurrences of
the macro name with its defined value or expression.
●​ #ifdef, #ifndef, #endif: These are used for conditional compilation. They allow specific
blocks of code to be included or excluded from compilation based on whether a macro is
defined (#ifdef) or not defined (#ifndef). This is useful for creating platform-specific code
or debugging sections.

A.6. What is the use of static variables in C?


The static keyword in C can be applied to local variables, global variables, and functions,
altering their linkage, storage duration, and scope.
●​ static Local Variables: When a variable inside a function is declared static, its lifetime
extends throughout the entire program execution, similar to a global variable. However, its
scope remains local to the function in which it is declared. This means the variable retains
its value across multiple calls to the same function, unlike regular local variables which
are re-initialized with each call. By default, static local variables are initialized to zero.
●​ static Global Variables/Functions: When declared globally or at file scope, static
variables or functions have internal linkage, meaning they are only visible and accessible
within the file where they are defined. This prevents name collisions when linking multiple
source files and promotes modularity by encapsulating data or functions within a specific
file. The use of static variables is particularly beneficial for maintaining state within a
function across calls without resorting to global variables, thereby improving
encapsulation.

A.7. Differentiate between malloc() and calloc() in C.


Both malloc() and calloc() are standard library functions in C, declared in stdlib.h, used for
dynamic memory allocation during the program's runtime from the heap segment. While they
serve a similar purpose, they have distinct differences in their behavior and usage:
Parameter malloc() calloc()
Definition Allocates a single block of Allocates multiple blocks of
memory of a specified size. memory, each of a specified
size, and initializes them to
zero.
Arguments Takes one argument: the total Takes two arguments: the
size in bytes to allocate (e.g., number of blocks to allocate
sizeof(int) * count). and the size of each block (e.g.,
count, sizeof(int)).
Initialization Does not initialize the allocated Initializes all allocated memory
memory; it contains "garbage" bytes to zero.
values (whatever was
previously in that memory
location).
Speed Generally faster, as it doesn't Generally slower due to the
perform initialization. additional step of initializing
Parameter malloc() calloc()
memory to zero.
Efficiency High time efficiency. Low time efficiency.
Usage Used for general-purpose Used for contiguous memory
memory allocation when allocation, often for arrays,
initialization is not required or where zero-initialization is
will be handled manually. desired.
Understanding these differences is vital for efficient memory management in C, as choosing the
appropriate function can impact performance and prevent subtle bugs related to uninitialized
memory.

A.8. Explain pointers in C and their uses.


Pointers in C are powerful variables that do not store direct values but instead hold the memory
address of another variable. They are fundamental to C programming, enabling low-level
memory manipulation and highly efficient operations. When a pointer is dereferenced, it
provides access to the value stored at the memory address it holds. The primary motivations for
using pointers include optimizing memory usage and enhancing program execution speed.
The various uses of pointers include:
●​ Passing Arguments by Reference: Pointers allow functions to modify the actual value of
a variable passed from the calling function, rather than just working on a copy. This is
essential for functions that need to return multiple values or modify input parameters
directly.
●​ Accessing Array Elements: Pointers provide an efficient way to traverse and access
elements within arrays. Array names themselves can often decay into pointers to their first
element, allowing pointer arithmetic to move through array elements.
●​ Returning Multiple Values from Functions: Although a C function can only directly
return a single value, pointers offer an indirect mechanism to return or modify multiple
values by passing variable addresses to the function.
●​ Dynamic Memory Allocation: Pointers are indispensable for dynamic memory
allocation, where memory is allocated during the program's runtime from the heap
segment. Functions like malloc() and calloc() return pointers to the newly allocated
memory, which can then be used to store data.
●​ Implementing Data Structures: Complex data structures such as linked lists, trees,
graphs, and hash tables heavily rely on pointers to establish connections between nodes
and manage their relationships in memory.
●​ System-Level Programming: Pointers are crucial for system-level programming tasks,
where direct interaction with memory addresses is necessary, such as in operating
systems, device drivers, and embedded systems. The ability to directly interact with
memory through pointers is a core aspect of C's power and its designation as a mid-level
language.

A.9. What is typedef in C?


typedef is a keyword in C used to create an alias, or a new name, for an existing data type. It
does not create a new type but rather provides a synonym for an existing one. This feature
significantly enhances code readability and portability, especially when dealing with complex
data type declarations, such as structures, unions, or function pointers. For instance, typedef
unsigned long int uli; allows uli to be used in place of unsigned long int, making declarations
shorter and clearer.

A.10. What is the difference between type casting and type


conversion?
Type casting and type conversion both involve changing a variable's data type, but they differ in
how and when the conversion occurs:
●​ Type Casting: This is an explicit conversion performed by the programmer. It involves
using a casting operator to force a value of one data type to be treated as another. For
example, (int)float_variable explicitly converts a floating-point value to an integer. This is a
deliberate action by the developer to change the type for a specific operation.
●​ Type Conversion (Coercion): This is an implicit conversion performed automatically by
the compiler. It occurs when an operation involves operands of different data types, and
the compiler automatically converts one type to another to ensure compatibility. For
example, in an expression like int_variable = float_variable;, the float_variable will be
implicitly converted to an int before assignment. This automatic adjustment by the
compiler ensures that operations can proceed without type mismatch errors.

A.11. What are functions and their types in C?


Functions in C are self-contained blocks of code designed to perform a specific task. They are
fundamental for modular programming, allowing complex problems to be broken down into
smaller, manageable units. This approach promotes code reusability, reduces redundancy, and
makes programs easier to understand, debug, and maintain.
Functions in C can be broadly categorized into two types:
●​ User-defined Functions: These are functions created and defined by the programmer to
fulfill specific requirements of their program. They allow developers to encapsulate
custom logic and reuse it multiple times within the same program or across different
programs.
●​ Built-in Functions (Library Functions): These are predefined functions that are part of
the C standard library, provided by the compiler package. They offer a wide range of
functionalities for common tasks, such as input/output operations (printf(), scanf()), string
manipulation (strcpy(), strlen()), mathematical calculations (sqrt(), sin()), and memory
management (malloc(), free()). Developers simply need to include the appropriate header
file to use these functions.

A.12. What is the difference between macro and functions in C?


Macros and functions are both mechanisms for code reuse in C, but they operate at different
stages of program processing and have distinct characteristics:
Feature Macro Function
Processing Preprocessed: The Compiled: Functions are
preprocessor performs a direct compiled as separate blocks of
textual substitution of the macro code.
name with its defined code
Feature Macro Function
before compilation.
Code Length Increases code length: Each Code length remains
instance of a macro call is unaffected: A single copy of the
replaced by its full definition, function code exists, and calls
leading to code expansion to it involve transferring control.
(inline code).
Execution Speed Faster: No function call Slower: Involves function call
overhead (stack frame creation, overhead, which includes
argument passing) as the code pushing arguments onto the
is expanded inline. stack, saving registers, and
jumping to the function's
address.
Type Checking No type checking: Macros Type checking is performed:
perform simple text The compiler checks for type
replacement, so the compiler compatibility of arguments
does not check for type passed to functions, ensuring
compatibility of arguments. This type safety.
can lead to subtle bugs.
Debugging Harder to debug: Because Easier to debug: Functions are
macros are expanded before distinct units of code, allowing
compilation, debugger often for straightforward debugging
sees the expanded code, with breakpoints and variable
making it difficult to set inspection.
breakpoints or inspect variables
within the macro definition.
Arguments Arguments are substituted Arguments are passed by value
directly into the macro text. or reference.
The choice between a macro and a function depends on the specific requirements, balancing
performance needs with considerations for type safety, debugging, and code size.

A.13. How do you convert a number to a string and a string to a


number in C?
Converting between numbers and strings is a common task in C programming, often required
for input/output operations or data manipulation.
●​ Number to String: The sprintf() function, part of the stdio.h library, is commonly used for
this conversion. It works similarly to printf() but instead of printing to the console, it
formats and stores a series of characters and values into a character array (string buffer).
○​ Example: int num = 123; char str; sprintf(str, "%d", num); This converts the integer
num into its string representation and stores it in str.
●​ String to Number: C provides several functions in the stdlib.h library for converting
strings to numerical types:
○​ atoi() (ASCII to Integer): Converts a string to an integer.
○​ atof() (ASCII to Float): Converts a string to a double-precision floating-point number.
○​ atol() (ASCII to Long): Converts a string to a long integer.
○​ Example: char str = "456"; int num = atoi(str); This converts the string str into its
integer representation and stores it in num.

A.14. What is recursion in C?


Recursion in C is a programming technique where a function calls itself, either directly or
indirectly, to solve a problem. A recursive function breaks down a problem into smaller, similar
subproblems until a base case is reached, which can be solved without further recursion. Each
recursive call adds a new stack frame to the call stack, utilizing a Last In First Out (LIFO)
structure. This means that every recursive call requires additional space in the stack memory to
store local variables and return addresses.
While elegant for certain problems (e.g., tree traversals, factorial calculation), excessive
recursion can lead to stack overflow errors if the recursion depth is too large, as it consumes
significant stack memory.

A.15. Why doesn't C support function overloading?


C does not support function overloading, a feature common in languages like C++. This
limitation stems from C's simpler function naming and linking mechanism. In C, each function
must have a unique name within its scope. The compiler and linker identify functions solely by
their names. If multiple functions had the same name but different parameters (which is what
overloading allows), the C compiler would not be able to differentiate between them during
compilation, nor would the linker be able to resolve which specific function to call at link time.
This design choice prioritizes simplicity and directness over flexibility in function signatures.

A.16. What is the use of printf() and scanf() functions? Also explain
format specifiers.
printf() and scanf() are fundamental input/output functions in C, declared in the stdio.h header
file, used for interacting with the console.
●​ printf(): This function is used to print formatted output to the standard output device,
typically the console. It can display text, numerical values, and character data according
to specified formats.
○​ Example: printf("Hello, World!\n");
●​ scanf(): This function is used to read formatted input from the standard input device,
typically the keyboard. It reads data according to specified format specifiers and stores it
into variables.
○​ Example: int age; scanf("%d", &age); Format Specifiers: These are special
characters used within the format string of printf() and scanf() to indicate the type of
data being read or written. They act as placeholders for variables.
●​ %d: Used for integer values.
●​ %s: Used for strings.
●​ %c: Used for single characters.
●​ %f: Used for floating-point values (float or double). These functions, along with format
specifiers, provide a versatile way to handle user interaction and display program results.

A.17. What's the value of the expression 5["abxdef"]?


The value of the expression 5["abxdef"] is 'f'. This seemingly unusual syntax is valid in C and
C++ due to the commutative nature of array indexing and pointer arithmetic. In C, a[b] is
internally equivalent to *(a + b), which means "the value at the memory address a plus b."
Because addition is commutative, *(a + b) is the same as *(b + a), which can then be rewritten
as b[a]. Therefore, 5["abxdef"] is equivalent to "abxdef". The string "abxdef" is treated as a
character array, and `` accesses the character at index 5 (0-indexed) of that array. "a" (index 0),
"b" (index 1), "x" (index 2), "d" (index 3), "e" (index 4), "f" (index 5). Thus, the character at index
5 is 'f'. This question tests a deeper understanding of pointer arithmetic and array decay in C.

B. C++ Programming & OOP Concepts


C++ builds upon the foundations of C, introducing powerful object-oriented programming (OOP)
paradigms that enable more structured, modular, and reusable code. The questions in this
section explore these advancements, particularly focusing on how C++ addresses the
complexities of larger software projects through its OOP features.

B.1. What is the difference between C and C++?


C and C++ are both powerful programming languages, with C++ being developed as an
extension of C, incorporating object-oriented capabilities. The differences highlight C++'s
evolution to support more complex software design principles:
Feature C Programming Language C++ Programming Language
Programming Paradigm Primarily procedural-oriented, Partially object-oriented,
focusing on functions and emphasizing data and objects.
steps.
Approach Follows a top-down approach Follows a bottom-up approach,
to program design. building from components.
Overloading Does not support function or Supports both function and
operator overloading. operator overloading.
Virtual/Friend Functions Does not support virtual or Supports both virtual and friend
friend functions. functions.
Keywords Has 32 keywords. Contains 52 keywords.
Memory Management Uses malloc(), calloc(), and Uses new and delete operators
free() for dynamic memory. for dynamic memory, which
also manage object
constructors/destructors.
Exception Handling No built-in exception handling Supports exception handling
mechanisms. using try/catch blocks.
String Handling Handles strings as character Provides a dedicated std::string
arrays (char). class for easier string
manipulation.
Pointers/References Supports pointers only. Supports both pointers and
reference variables.
The transition from C's procedural nature to C++'s object-oriented capabilities represents a
significant shift in how software is designed and managed. C++'s features enable developers to
create more maintainable, scalable, and robust applications by encapsulating data with behavior
and promoting code reuse through inheritance and polymorphism.

B.2. What are classes and objects in C++?


Classes and objects are fundamental concepts in Object-Oriented Programming (OOP) in C++.
They provide a structured way to organize code and data.
●​ Class: A class is a user-defined blueprint or a template for creating objects. It defines the
structure and behavior that its objects will possess. A class encapsulates data members
(variables that hold state) and member functions (methods that define behavior) into a
single logical unit. It acts as a specification for creating objects, much like a blueprint for a
house specifies its design without being the house itself.
○​ Example: A Car class might define properties like color, make, model (data
members) and actions like start(), stop(), accelerate() (member functions).
●​ Object: An object is an instance of a class. It is a real-world entity that has a state (values
of its data members) and behavior (actions performed by its member functions). When an
object is created from a class, memory is allocated for its data members, and it can then
interact with its member functions. Multiple objects can be created from a single class,
each with its own unique state.
○​ Example: myCar and yourCar could be two different objects of the Car class, each
with its own color, make, and model. The relationship between classes and objects
is central to C++'s object-oriented paradigm, enabling data encapsulation and
modular design.

B.3. What are access modifiers in C++?


Access modifiers (also known as access specifiers) in C++ are keywords that control the
visibility and accessibility of class members (data members and member functions) from outside
the class. They are crucial for implementing encapsulation, an OOP principle that promotes data
hiding and protects data from unauthorized access. There are three types of access modifiers:
●​ public: Members declared under the public specifier are accessible from anywhere in the
program, both from within the class itself and from outside the class scope. They form the
interface through which objects interact with the class.
●​ private: Members declared as private are accessible only from within the same class.
They cannot be accessed directly from outside the class, even by objects of that class.
This is the default access level for members of a class. Private members are typically
used to hide implementation details and protect the internal state of an object.
●​ protected: Members declared as protected are accessible from within the class itself and
by its derived classes (classes that inherit from it). They are not directly accessible from
outside the class by non-derived classes. This access level is primarily used in
inheritance hierarchies to allow derived classes to access certain base class members
while keeping them hidden from the public. The strategic use of access modifiers is
fundamental to designing secure and maintainable C++ applications by enforcing
controlled access to data.

B.4. Difference between equal to (==) and assignment operator (=)?


The == and = symbols in C++ serve entirely different purposes, despite their visual similarity:
●​ == (Equal To Operator): This is a comparison operator. It is used to check whether two
values or expressions are equal. It returns a Boolean value: true if the operands are
equal, and false otherwise.
○​ Example: if (x == 10) checks if the value of x is equal to 10.
●​ = (Assignment Operator): This is an assignment operator. It is used to assign the value
of the expression on the right-hand side to the variable on the left-hand side.
○​ Example: x = 10; assigns the value 10 to the variable x. Confusing these two
operators is a common mistake that can lead to logical errors in C++ programs.

B.5. What is the difference between a while loop and a do-while loop?
Both while and do-while loops are used for iteration in C++, allowing a block of code to be
executed repeatedly as long as a specified condition is true. Their primary difference lies in
when the condition is evaluated:
●​ while loop: This is an entry-controlled loop. The condition is checked before the loop
body is executed for the first time. If the condition evaluates to false initially, the loop body
will not execute even once.
○​ Syntax: while (condition) { // code to execute }
●​ do-while loop: This is an exit-controlled loop. The loop body is executed at least once,
and then the condition is checked at the end of the iteration. If the condition evaluates to
false after the first iteration, the loop terminates.
○​ Syntax: do { // code to execute } while (condition); The do-while loop is suitable
when the code block must be executed at least once, regardless of the initial
condition, such as in menu-driven programs where the menu needs to be displayed
at least once.

B.6. What are the four different data types in C++?


C++ organizes data types into several categories to provide flexibility and control over how data
is stored and manipulated:
●​ Primitive/Basic Data Types: These are the fundamental built-in data types directly
supported by the language. They include char (for characters), int (for integers), short,
float (for single-precision floating-point numbers), double (for double-precision
floating-point numbers), long, and bool (for Boolean values true or false).
●​ Derived Data Types: These types are built upon or derived from the primitive data types.
Examples include Arrays (collections of elements of the same type) and Pointers
(variables that store memory addresses).
●​ Enumeration Data Types (enum): These are user-defined data types that consist of a
set of named integer constants. They provide a way to assign meaningful names to
integral values, enhancing code readability and maintainability.
●​ User-defined Data Types: These are complex data types created by the programmer to
model real-world entities or concepts. The most common user-defined types are struct
(structures) and class (classes), which allow for the grouping of related data members
and member functions into a single unit.

B.7. How is struct different from class in C++?


In C++, both struct and class are used to define user-defined data types that can encapsulate
data members and member functions. While they can often be used interchangeably, there are
key differences in their default behaviors:
Feature struct class
Default Member Access Members (data and functions) Members are private by default.
are public by default.
Default Inheritance Access When deriving a struct from a When deriving a class from a
class or struct, the default class or struct, the default
access specifier for inheritance access specifier for inheritance
is public. is private.
The choice between struct and class often comes down to convention and the intended use
case. struct is typically used for plain old data (POD) structures where all members are intended
to be public, while class is preferred for encapsulating data and behavior, with a focus on data
hiding through private or protected members.

B.8. Explain polymorphism (compile-time vs. runtime) with examples


in C++.
Polymorphism, an essential concept in Object-Oriented Programming (OOP), means "many
forms." In C++, it allows a single interface or function name to be used for different types of
objects or data, with the specific implementation executed depending on the context. This
concept typically manifests in class hierarchies linked by inheritance.
Polymorphism can be achieved at two different stages:
●​ Compile-time Polymorphism (Static Polymorphism): In this form, the decision about
which function to execute is made by the compiler during compilation. This leads to
quicker execution because the method call is resolved early. Compile-time polymorphism
is achieved through:
○​ Function Overloading: Defining multiple functions with the same name but
different parameters (number, type, or order). The compiler determines which
function to call based on the arguments provided.
○​ Operator Overloading: Redefining the behavior of existing C++ operators (like +, -,
*, ==) for user-defined data types.
■​ Example (Operator Overloading):​
class A {​
public:​
// Assume operator+ is overloaded for class A​
A operator+(const A& other) {​
// Implementation for addition of A objects​
return A();​
}​
};​
int main() {​
A a1, a2, a3;​
a3 = a1 + a2; // The '+' operator is resolved at
compile time for A objects​
return 0;​
}​
Here, the compiler knows exactly which operator+ to call at compile time.
●​ Runtime Polymorphism (Dynamic Polymorphism): In this form, the decision about
which function to execute is delayed until runtime. This typically results in slower
execution due to the overhead of dynamic dispatch (e.g., virtual function table lookup).
Runtime polymorphism is primarily achieved through:
○​ Function Overriding: A derived class provides its own specific implementation for
a function that is already defined in its base class.
○​ Virtual Functions: Declared with the virtual keyword in the base class, virtual
functions enable dynamic method dispatch. When a base class pointer or reference
points to a derived class object, calling a virtual function through that
pointer/reference will execute the derived class's version of the function, ensuring
the correct behavior based on the object's actual type at runtime.
■​ Example (Virtual Function):​
class Animal {​
public:​
virtual void makeSound() {​
std::cout << "Animal makes a sound." <<
std::endl;​
}​
};​
class Dog : public Animal {​
public:​
void makeSound() override { // 'override' keyword is
optional but good practice​
std::cout << "Dog barks." << std::endl;​
}​
};​
int main() {​
Animal* myAnimal = new Dog(); // Base class pointer
pointing to derived class object​
myAnimal->makeSound(); // Calls Dog's makeSound() at
runtime​
delete myAnimal;​
return 0;​
}​
In this example, myAnimal->makeSound() calls the Dog's makeSound
method because makeSound is virtual and myAnimal points to a Dog object
at runtime. Understanding the distinction between compile-time and runtime
polymorphism is crucial for designing flexible and extensible class hierarchies
in C++.

B.9. Compare new and malloc in C++.


In C++, both new and malloc() are used for dynamic memory allocation, but new is an operator
specific to C++ and is generally preferred for allocating objects, while malloc() is a C library
function. Their differences are significant, especially in an object-oriented context:
Feature new Operator malloc() Function
Type An operator (keyword) in C++. A library function from stdlib.h
(inherited from C).
Feature new Operator malloc() Function
Constructors Automatically calls the Does not call constructors; it
constructor for objects being simply allocates raw memory.
allocated, ensuring proper
initialization.
Memory Size Automatically calculates the Requires explicit specification
memory size required for the of the memory size in bytes
object(s) based on the type. No (e.g., sizeof(type) * count).
need to specify size in bytes.
Return Type Returns a type-safe pointer to Returns a void* pointer, which
the allocated object(s). No requires explicit type casting to
explicit type casting is needed. the desired data type.
Failure Handling Throws a std::bad_alloc Returns NULL on allocation
exception on allocation failure failure.
(by default).
Destructors Paired with the delete operator, Paired with free() function,
which automatically calls the which only deallocates memory
destructor for the object(s) and does not call destructors.
before deallocating memory.
Overloading Can be overloaded for custom Cannot be overloaded.
memory management.
Memory Allocation Allocates memory and then Only allocates memory.
constructs the object in that
memory.
The new operator is deeply integrated with C++'s object model, making it safer and more
convenient for object allocation as it handles construction and destruction automatically.
malloc(), while still available, is typically reserved for C-style memory management or when
dealing with raw memory buffers where object construction/destruction is not relevant.

B.10. What is the scope resolution operator (::) in C++ and when is it
used?
The scope resolution operator (::) in C++ is a powerful operator used to define or access
members of a class, namespace, or enumeration, or to access global variables that have been
shadowed by local variables. It clarifies the context (scope) of an identifier.
Common uses include:
●​ Defining a function outside a class: When a member function is declared inside a class
but its definition is provided outside the class, the :: operator is used to specify which
class the function belongs to.
○​ Example: void MyClass::myFunction() { /* definition */ }
●​ Accessing a global variable when a local variable has the same name: If a local
variable in a function has the same name as a global variable, the local variable takes
precedence. To explicitly access the global variable, the :: operator is used without a
preceding scope name (e.g., ::globalVariable). This is known as variable shadowing.
●​ Accessing static members of a class: :: is used to access static data members or static
member functions of a class without needing an object instance (e.g.,
ClassName::staticMember).
●​ Accessing members of a namespace: To access members (functions, variables,
classes) defined within a namespace (e.g., std::cout). The scope resolution operator is
crucial for managing naming conflicts and explicitly specifying the context of identifiers in
C++ programs.

B.11. Can you compile a program without the main function in C++?
No, a standard C++ program cannot be compiled and linked into an executable without a main
function. The main function serves as the entry point for program execution. When a C++
program is run, the operating system's loader looks for the main function to begin execution.
Without it, the linker will be unable to find the starting point of the program, resulting in a linking
error (not a compilation error). While individual .cpp files without a main function can be
compiled into object files, they cannot be directly executed as standalone programs.

B.12. What is std in C++?


std is a standard namespace in C++. A namespace is a declarative region that provides a scope
to the identifiers (names of types, functions, variables, etc.) inside it. The std namespace
contains the entire C++ Standard Library, which includes fundamental components such as:
●​ Input/output functionalities (e.g., std::cout for output, std::cin for input, std::endl for
newline).
●​ String manipulation classes (e.g., std::string).
●​ Containers (e.g., std::vector, std::map).
●​ Algorithms (e.g., std::sort).
●​ Other utilities. To use elements from the std namespace, you must either explicitly qualify
them with std:: (e.g., std::cout << "Hello";) or use a using declaration (e.g., using
namespace std;) to bring the entire namespace into scope. Using std:: explicitly is
generally considered better practice in larger projects to avoid name collisions.

B.13. What are pointers in C++?


Pointers in C++ are variables that store memory addresses, similar to their role in C. They are a
fundamental concept for direct memory access and manipulation. In C++, pointers are used for:
●​ Dynamic Memory Allocation: Along with the new and delete operators, pointers are
used to allocate and deallocate memory during program runtime from the heap,
particularly for objects.
●​ Passing Arguments by Reference: While C++ introduces reference variables, pointers
can still be used to pass arguments by reference to functions, allowing the function to
modify the original variable.
●​ Working with Arrays and Strings: Pointers can efficiently traverse arrays and character
strings.
●​ Implementing Data Structures: Pointers are essential for building complex data
structures like linked lists, trees, and graphs.
●​ Polymorphism (Runtime): Pointers to base classes are crucial for achieving runtime
polymorphism through virtual functions, allowing calls to derived class methods based on
the object's actual type. While C++ offers more high-level abstractions and features like
references that can sometimes reduce the direct need for pointers, understanding
pointers remains vital for performance optimization, low-level programming, and grasping
the underlying memory model.

C. JavaScript Fundamentals
JavaScript is a versatile, high-level, and dynamic programming language primarily known for its
role in web development, enabling interactive and dynamic content on websites. This section
covers core JavaScript concepts essential for a junior full-stack developer, from basic data types
to DOM manipulation and asynchronous operations.

C.1. What are the JavaScript data types?


JavaScript is a dynamically typed language, meaning variable types are determined at runtime.
It categorizes data into two main groups: primitive types and one non-primitive type.
Primitive Data Types: These are immutable values that are not objects and have no methods.
1.​ Number: Represents both integer and floating-point numbers. Examples: 10, 3.14, -5.
2.​ String: Represents textual data, enclosed in single or double quotes. Examples: "Hello
World", 'JavaScript'.
3.​ Boolean: Represents logical entities, with only two possible values: true or false.
4.​ Undefined: Represents a variable that has been declared but has not yet been assigned
a value. It's also the value returned by functions that do not explicitly return anything.
5.​ Null: Represents the intentional absence of any object value. It is a primitive value,
though typeof null returns "object" (a historical bug).
6.​ Symbol: (Introduced in ES6/ES2015) Represents a unique and immutable value.
Symbols are often used as unique property keys for objects to avoid name collisions.
7.​ BigInt: (Introduced in ES2020) Represents integers with arbitrary precision, allowing for
numbers larger than 2^53 - 1 (the maximum for Number).
Non-Primitive Data Type:
1.​ Object: This is a complex data type that can store collections of data and more complex
entities. Objects are mutable key-value pairs. Arrays and functions are also special kinds
of objects in JavaScript. Understanding these data types is foundational for working with
JavaScript, as they dictate how values behave and how operations can be performed on
them.

C.2. What are the differences between global and local variables?
How can too many global variables negatively affect your code?
The distinction between global and local variables in JavaScript relates to their scope, which
determines where they can be accessed in the code.
●​ Local Variables: These are declared inside a function. Their scope is limited to that
specific function, meaning they can only be accessed from within that function. Local
variables are created when the function is called and are destroyed when the function
finishes execution.
●​ Global Variables: These are declared outside any function or block. Their scope extends
throughout the entire program, making them accessible from any part of the code,
including within functions.
While global variables can seem convenient for widespread access, their overuse can lead to
several negative impacts on code quality and maintainability:
●​ Name Conflicts (Pollution of Global Namespace): In larger applications or when
integrating multiple third-party libraries, having many global variables significantly
increases the likelihood of variable name collisions. If two different parts of the code
declare a global variable with the same name, one will overwrite the other, leading to
unpredictable behavior and difficult-to-diagnose bugs.
●​ Maintainability Issues: Global variables introduce implicit dependencies across different
parts of the codebase. It becomes harder to track where a global variable might be
modified, making debugging and refactoring more challenging. Any function could
potentially alter a global variable, making it difficult to isolate issues.
●​ Reduced Modularity and Reusability: Functions that rely heavily on global variables are
less modular and harder to reuse in different contexts, as they depend on a specific global
state. This limits the independence of code components.
●​ Memory Leaks: Global variables persist in memory for the entire lifetime of the
application. If they hold large data structures or references to DOM elements, they can
inadvertently prevent garbage collection, leading to memory leaks and degraded
performance over time. For these reasons, it is generally considered a best practice to
minimize the use of global variables and encapsulate data within functions or modules
whenever possible.

C.3. What are the differences between the =, ==, and === operators in
JavaScript?
JavaScript provides three distinct operators for assignment and comparison, each with specific
behaviors regarding type handling:
Operator Name Purpose Type Coercion Example ("2" Example ("2"
== 2) === 2)
= Assignment Assigns the N/A let x = 2; N/A
Operator value of the
right operand to
the left
operand.
== Abstract Checks for Yes true false
Equality equality in
value only.
Attempts to
convert
operands to a
common type
before
comparison.
=== Strict Equality Checks for No false true
equality in both
value and type.
No type
conversion is
performed.
●​ = (Assignment Operator): This operator is used to assign a value to a variable. For
example, let fifty = 50; or let greeting = "Hey there!";.
●​ == (Abstract Equality Operator): This operator performs a "loose" equality check. It
compares two values for equality after performing type coercion. This means if the
operands are of different types, JavaScript will attempt to convert one or both operands to
a common type before making the comparison. For instance, console.log("2" == 2)
evaluates to true because the string "2" is converted to the number 2 before the
comparison. Similarly, console.log(0 == false) also evaluates to true.
●​ === (Strict Equality Operator): This operator performs a "strict" equality check. It
compares two values for equality without performing any type coercion. For an expression
to evaluate to true using ===, both the value and the data type of the operands must be
identical. For example, console.log("2" === 2) evaluates to false because, although the
values are numerically equivalent, their types (string and number) are different. However,
console.log(2 === 2) evaluates to true as both the value and the type are identical.
Similarly, console.log(0 === false) evaluates to false because their types differ. It is
generally recommended to use === for comparisons in JavaScript to avoid unexpected
behavior caused by type coercion, leading to more predictable and robust code.

C.4. How would you use escape characters to correctly log quotes in
a string? For example, "Him & I are "good" friends."
To include special characters, such as double quotes, within a string literal that is already
enclosed in double quotes, JavaScript uses the backslash (\) as an escape character. The
backslash signals that the character immediately following it should be interpreted literally rather
than as part of the string's syntax.
For the example "Him & I are "good" friends.", it can be correctly logged using escape
characters as follows: console.log('Him & I are "good" friends.'); Alternatively, if the string is
defined with double quotes, the internal double quotes must be escaped: console.log("Him & I
are \"good\" friends."); The backslash ensures that the inner double quotes are treated as part of
the string content rather than marking the end of the string literal.

C.5. Explain the .pop(), .push(), .slice(), and .splice() methods for
arrays.
JavaScript arrays come with several built-in methods for manipulating their contents.
Understanding these methods is crucial for effective data management. Let's consider an
example array: let bootdotdev = ["the", "best", "coding", "courses", "ever", "like", "totally"];.
●​ .pop(): This method removes the last element from an array and returns that removed
element. It directly modifies the original array (in-place).
○​ Example: let removed = bootdotdev.pop();
○​ After pop(): bootdotdev becomes ["the", "best", "coding", "courses", "ever", "like"],
and removed is "totally".
●​ .push(): This method adds one or more elements to the end of an array and returns the
new length of the array. It also modifies the original array (in-place).
○​ Example: let newLength = bootdotdev.push("definitely", "start", "it");
○​ After push(): bootdotdev becomes ["the", "best", "coding", "courses", "ever", "like",
"totally", "definitely", "start", "it"], and newLength is 10.
●​ .slice(start, end): This method returns a shallow copy of a portion of an array into a new
array object. The selection includes elements from the start index up to (but not including)
the end index. Crucially, .slice() does not modify the original array.
○​ Example: let slicedArray = bootdotdev.slice(1, 4);
○​ After slice(): slicedArray is ["best", "coding", "courses"], and bootdotdev remains
unchanged.
●​ .splice(start, deleteCount, item1, item2,...): This is a versatile method that changes the
contents of an array by removing or replacing existing elements and/or adding new
elements in place. It returns an array containing the deleted elements (if any).
○​ start: The index at which to start changing the array.
○​ deleteCount: The number of elements to remove from start.
○​ item1, item2,...: The elements to add to the array, starting at start.
○​ Example: let removedElements = bootdotdev.splice(2, 1, "amazing"); (Removes
"coding" and inserts "amazing" at index 2)
○​ After splice(): bootdotdev becomes ["the", "best", "amazing", "courses", "ever",
"like", "totally"], and removedElements is ["coding"]. These methods provide
powerful tools for dynamic array manipulation, with pop() and push() being efficient
for managing array ends, and slice() and splice() offering more granular control over
array segments.

C.6. What are higher-order functions, and what are their benefits?
Higher-order functions are a core concept in functional programming paradigms within
JavaScript. A function is considered higher-order if it satisfies at least one of the following
conditions:
●​ It takes one or more functions as arguments (known as callback functions).
●​ It returns a function as its result.
Examples of built-in higher-order functions in JavaScript include Array.prototype.map(),
Array.prototype.filter(), Array.prototype.reduce(), and setTimeout().
The benefits of using higher-order functions are significant for writing clean, modular, and
reusable code:
●​ Code Reusability: They enable the creation of generic functions that can operate on
different data types or behaviors by accepting functions as parameters. This reduces code
duplication and promotes a more abstract way of thinking about operations.
●​ Abstraction: Higher-order functions allow developers to abstract away common patterns
or repetitive logic. Instead of writing similar loops or conditional statements multiple times,
a higher-order function can encapsulate that logic, making the code cleaner and easier to
understand.
●​ Modularity: They facilitate breaking down complex problems into smaller, more
manageable functions. Each function can focus on a single, well-defined task, improving
the overall structure and readability of the codebase.
●​ Functional Programming Style: They are fundamental to adopting a functional
programming style, which often leads to more predictable and testable code by
minimizing side effects and emphasizing pure functions. This approach is particularly
powerful for data transformations and processing. By leveraging higher-order functions,
developers can write more expressive and efficient JavaScript code, particularly in
scenarios involving data manipulation and asynchronous operations.
C.7. How do break and continue statements differ, and where would
you use each?
break and continue are control flow statements used within loops in JavaScript (and other
programming languages) to alter their normal execution. They serve distinct purposes:
Feature break continue
Purpose Terminates the innermost loop Skips the current iteration of the
immediately. loop.
Flow Control Transfers control to the Transfers control to the next
statement immediately iteration of the loop, skipping
following the loop. any remaining code in the
current iteration.
Usage Used when a specific condition Used when a specific condition
is met that requires exiting the is met that requires skipping
loop entirely, regardless of only the rest of the current
whether the loop's original iteration, but the loop should
condition is still true. continue with subsequent
iterations.
Examples:
●​ break example:​
for (let i = 0; i < 100; i++) {​
if (i === 10) {​
console.log("Loop stopped at 10.");​
break; // Exits the loop entirely when i is 10​
}​
console.log("Current number (break): " + i);​
}​
// Output will go from 0 to 9, then "Loop stopped at 10."​
In this scenario, break is used to end the loop prematurely when a specific target value is
found or a critical condition is met.
●​ continue example:​
for (let i = 0; i < 10; i++) {​
if (i % 2 === 0) {​
continue; // Skips the rest of the current iteration if i
is even​
}​
console.log("Current number (continue): " + i);​
}​
// Output will be: 1, 3, 5, 7, 9 (only odd numbers are printed)​
Here, continue is used to skip processing for even numbers, allowing the loop to proceed
to the next iteration without executing the console.log for even values. Choosing between
break and continue depends on whether the goal is to exit the loop entirely or simply to
skip a single iteration.

C.8. What is JSON? And how would you convert JSON strings into
objects, or convert an object into a JSON string?
JSON (JavaScript Object Notation) is a lightweight, human-readable, and
language-independent data-interchange format. It is widely used for transmitting data between a
web server and a web application, and for storing data. JSON is built on two universal
structures:
1.​ A collection of name/value pairs (in JavaScript, this corresponds to an Object).
2.​ An ordered list of values (in JavaScript, this corresponds to an Array).
JSON's simplicity and direct mapping to JavaScript objects make it an ideal format for web
communication.
JavaScript provides built-in methods for working with JSON:
●​ Converting JSON String to JavaScript Object: The JSON.parse() method is used to
parse a JSON string, constructing the JavaScript value or object described by the string.
○​ Example:​
let jsonString = '{"name": "Bishal", "age": 22, "isStudent":
true}';​
let jsObject = JSON.parse(jsonString);​
console.log(jsObject.name); // Output: Bishal​
console.log(jsObject.age); // Output: 22​

●​ Converting JavaScript Object to JSON String: The JSON.stringify() method is used to


convert a JavaScript value (usually an object or array) into a JSON string. This is
commonly done when sending data to a web server.
○​ Example:​
let jsObject = { name: "Bishal", age: 22, skills: };​
let jsonString = JSON.stringify(jsObject);​
console.log(jsonString); // Output:
{"name":"Bishal","age":22,"skills":}​

These methods are essential for handling data exchange in modern web applications.

C.9. What are arrow functions and what benefits do they bring in your
code?
Arrow functions, introduced in ECMAScript 6 (ES6), provide a more concise syntax for writing
function expressions in JavaScript. They are often used for shorter, anonymous functions.
Syntax: Traditional function:
function(parameters) {​
// function body​
}​

Arrow function:
(parameters) => {​
// function body​
}​

For single-expression functions, the curly braces and return keyword can be omitted for an
implicit return: parameters => expression
Benefits of Arrow Functions:
●​ Concise Syntax: They offer a shorter and more readable syntax, especially for simple
one-liner functions, which can make code less verbose and easier to scan.
●​ Lexical this Binding: This is one of the most significant benefits. Unlike traditional
functions, arrow functions do not have their own this context. Instead, this is lexically
bound, meaning it inherits this from the enclosing (parent) scope at the time the function
is defined. This solves common this binding issues and the need for bind(), call(), or
apply() or storing this in a variable (e.g., that = this) in callback functions or event
handlers.
●​ No arguments Object: Arrow functions do not have their own arguments object. They
access the arguments object of their enclosing function.
●​ Not Suitable as Constructors: Arrow functions cannot be used as constructors with the
new keyword. Arrow functions are widely adopted in modern JavaScript development for
their conciseness and predictable this behavior, simplifying asynchronous code and
callbacks.

C.10. What is the isNaN function?


The isNaN() function in JavaScript is a global function used to determine whether a value is
"Not-a-Number" (NaN). It returns true if the value passed to it is NaN or if it can be coerced into
NaN (e.g., isNaN("hello") is true). It returns false if the value is a valid number (including Infinity
and -Infinity).
It is important to note that NaN is a unique value in JavaScript in that it is not equal to itself
(NaN === NaN evaluates to false). Therefore, isNaN() is the reliable way to check for NaN
values.
●​ Example:​
console.log(isNaN(123)); // false (123 is a number)​
console.log(isNaN("hello")); // true ("hello" cannot be
converted to a number)​
console.log(isNaN("123")); // false ("123" can be converted
to a number)​
console.log(isNaN(NaN)); // true (NaN is NaN)​
console.log(isNaN(0 / 0)); // true (result of 0/0 is NaN)​
console.log(isNaN(true)); // false (true is coerced to 1)​
console.log(isNaN(null)); // false (null is coerced to 0)​
console.log(isNaN(undefined)); // true (undefined is coerced to
NaN)​

The isNaN() function is crucial for validating numerical inputs and preventing errors in
calculations.

C.11. What are the differences between undeclared and undefined


variables in JavaScript?
While both terms relate to variables without a clear value, "undeclared" and "undefined" refer to
distinct states in JavaScript:
●​ Undeclared Variable: An undeclared variable is a variable that has not been formally
declared using any of the declaration keywords (var, let, or const) before being accessed.
Attempting to use or access an undeclared variable will result in a ReferenceError at
runtime, as the JavaScript engine cannot find any binding for that variable in the current
scope chain.
○​ Example: console.log(myUndeclaredVar); // Throws ReferenceError:
myUndeclaredVar is not defined
●​ Undefined Variable: An undefined variable is a variable that has been declared using
var, let, or const, but has not yet been assigned a value. In this case, the variable exists,
but its value is undefined. Functions that do not explicitly return a value also implicitly
return undefined.
○​ Example: let myUndefinedVar; console.log(myUndefinedVar); // Output: undefined
○​ Example: var anotherUndefinedVar = undefined; console.log(anotherUndefinedVar);
// Output: undefined The distinction is critical for debugging and understanding
variable lifecycle. An undefined variable is a recognized state, whereas an
undeclared variable signifies a fundamental syntax or scope issue.

C.12. What is the DOM? And what does it do?


The DOM (Document Object Model) is a programming interface for web documents (HTML
and XML). It represents the structure of a web page as a logical tree of objects, where each part
of the document—such as HTML elements, attributes, and text content—is represented as a
"node" in this tree. This hierarchical structure allows programmatic access to the page's content
and structure.
The primary purpose of the DOM is to enable JavaScript (and other scripting languages) to
interpret and interact with the HTML document dynamically. Through the DOM, JavaScript can:
●​ Access and Change HTML Elements: Locate specific elements on the page (e.g., by
ID, class, tag name) and modify their content, attributes, or styles.
●​ Change CSS Styles: Dynamically apply or remove CSS styles to elements, altering their
appearance.
●​ Create and Remove HTML Elements: Add new elements to the page or remove existing
ones, allowing for dynamic content generation.
●​ Add and Handle Events: Attach event listeners to elements (e.g., for clicks, key presses,
form submissions) and define functions to execute when those events occur. In essence,
the DOM provides an object-oriented representation of the web page, turning a static
HTML document into a dynamic, interactive application that can respond to user actions
and update its content in real-time. It acts as the bridge between the static web page and
the dynamic scripting capabilities of JavaScript.

C.13. How do you manipulate the DOM using JavaScript? Provide


examples.
Manipulating the DOM with JavaScript involves selecting elements, modifying their properties,
content, or attributes, and dynamically adding or removing elements. This capability is central to
creating interactive web applications.
The general process involves:
1.​ Selecting Elements: Before manipulation, an element must be selected and a reference
to it stored in a variable. The Document.querySelector() method is commonly used,
allowing selection based on CSS selectors. For multiple elements,
Document.querySelectorAll() returns a NodeList.
○​ Example:​
const myLink = document.querySelector('a'); // Selects the
first <a> element​
const allParagraphs = document.querySelectorAll('p'); //
Selects all <p> elements​
This approach provides a modern and flexible way to target specific parts of the
web page.
2.​ Changing Text Content: The Node.textContent property allows modification of the text
inside an element.
○​ Example:​
myLink.textContent = "Visit Mozilla Docs"; // Changes the
visible text of the link​
This is a direct way to update the content displayed to the user.
3.​ Changing Attributes: Element attributes, such as href for links or src for images, can be
modified directly or using Element.setAttribute().
○​ Example:​
myLink.href = "https://developer.mozilla.org"; // Changes the
URL the link points to​
const myParagraph = document.createElement('p');​
myParagraph.setAttribute('class', 'highlight'); // Adds a CSS
class to the paragraph​
Manipulating attributes is crucial for dynamic styling, linking, and functionality.
4.​ Creating and Placing New Nodes: New HTML elements can be created using
Document.createElement() and then added to the DOM using methods like
Node.appendChild().
○​ Example:​
const newParagraph = document.createElement('p');​
newParagraph.textContent = "This is a dynamically added
paragraph.";​
const sectionElement = document.querySelector('section');​
sectionElement.appendChild(newParagraph); // Appends the new
paragraph to a <section>​
This allows for dynamic content generation based on user interaction or data
changes.
5.​ Moving and Removing Elements: Elements can be moved within the DOM using
Node.appendChild() (if the element already exists, it's moved, not copied). Elements can
be removed using Node.removeChild() (requires parent reference) or Element.remove()
(more direct).
○​ Example (Removing):​
// Assuming newParagraph is already in the DOM and
sectionElement is its parent​
sectionElement.removeChild(newParagraph); // Removes the
paragraph​
// Or, if only reference to the element itself:​
// newParagraph.remove();​
This capability is vital for managing dynamic layouts and user interfaces.
6.​ Manipulating Styles: Inline styles can be applied directly via the HTMLElement.style
property.
○​ Example:​
newParagraph.style.color = "white";​
newParagraph.style.backgroundColor = "black";​
newParagraph.style.padding = "10px";​
It is important to note that JavaScript property names for CSS styles are typically in
camelCase (e.g., backgroundColor) while CSS uses hyphenated names (e.g.,
background-color). For larger applications, managing styles by adding/removing
CSS classes is often preferred for better separation of concerns.
These manipulation techniques are the foundation for building interactive and responsive web
experiences.

C.14. Explain event bubbling and event capturing in JavaScript.


Event bubbling and event capturing are two distinct phases of event propagation in the HTML
Document Object Model (DOM) API. They define the order in which elements receive and
process an event when it occurs on an element nested within other elements, and multiple
elements have registered handlers for that event.
The W3C DOM Events standard describes three phases of event propagation:
1.​ Capturing Phase (Trickling Down): In this phase, the event starts from the outermost
ancestor element (e.g., window, document, <html>, <body>) and travels downwards
through the DOM tree, through each intermediate parent element, until it reaches the
immediate parent of the target element where the event originated. Handlers registered
for the capturing phase execute during this downward journey.
2.​ Target Phase: The event reaches the actual element on which it occurred. Handlers
attached directly to this target element execute during this phase.
3.​ Bubbling Phase (Bubbling Up): After reaching the target element, the event then
propagates upwards from the target element to its immediate parent, and continues
bubbling up through successive parent elements until it reaches the root of the document
(e.g., <body>, <html>, document, window). Handlers registered for the bubbling phase
execute during this upward journey.
Example: Consider a <div> containing a <ul> which contains an <li>. If a click event occurs on
the <li>:
●​ Capturing: The event would first be handled by the <div>'s click handler (if registered for
capturing), then the <ul>'s handler (if registered for capturing), and finally reach the <li>.
●​ Bubbling: After reaching <li>, the event would then be handled by the <li>'s click handler
(if registered for bubbling), then the <ul>'s handler (if registered for bubbling), and finally
the <div>'s handler (if registered for bubbling).
Registering Event Handlers: The addEventListener(type, listener, useCapture) method is used
to register event handlers.
●​ If useCapture is false (default), the handler is registered for the bubbling phase.
●​ If useCapture is true, the handler is registered for the capturing phase.
When to Use Which:
●​ Bubbling is more commonly used and is the default. It is particularly useful for event
delegation, where a single event listener on a parent element can handle events for
many child elements. This reduces the number of event listeners, improving performance.
●​ Capturing is less common but can be useful in specific scenarios, such as when you
need to intercept an event before it reaches its target element, or if a child element's code
might stop event propagation during the bubbling phase. It is important to note that not all
events bubble (e.g., the focus event).

C.15. What is AJAX?


AJAX (Asynchronous JavaScript and XML) is a set of web development techniques that
allows web pages to update content asynchronously, meaning that data can be exchanged with
a server and specific parts of a web page can be updated without requiring a full page reload.
This results in a more interactive, responsive, and dynamic user experience, as only necessary
data is transferred between the client and server.
AJAX is not a single technology but a combination of several existing web technologies working
together:
●​ JavaScript: The primary scripting language used to make asynchronous requests and
manipulate the DOM.
●​ XMLHttpRequest (XHR) Object: The core component that enables asynchronous
communication with the server in the background. It sends HTTP requests and handles
responses.
●​ HTML and CSS: Used for structuring and styling the web page.
●​ DOM (Document Object Model): Used by JavaScript to dynamically update specific
parts of the web page based on the data received from the server.
●​ XML (and now predominantly JSON): Used as a data interchange format between the
client and server. While "XML" is in the name, JSON has largely replaced XML due to its
lighter weight and native compatibility with JavaScript objects.
How AJAX Works (Simplified):
1.​ A user event (e.g., button click, text input) or a programmatic trigger initiates an AJAX
request from the browser.
2.​ JavaScript creates an XMLHttpRequest object (or uses the modern fetch API).
3.​ The XMLHttpRequest object sends an asynchronous HTTP request to the server in the
background. The user can continue interacting with the page.
4.​ The server processes the request and sends a response back to the browser, typically in
JSON or XML format.
5.​ The XMLHttpRequest object receives the response.
6.​ A JavaScript callback function processes the received data and uses the DOM to update
only the relevant parts of the web page, without a full page refresh.
Examples of AJAX in action:
●​ Social Media Feeds: Incrementing "Like" counts or loading new posts without reloading
the entire page (e.g., Facebook, Twitter).
●​ Search Engine Autosuggest: Providing real-time search suggestions as a user types
into a search bar (e.g., Google Search).
●​ Form Submissions: Submitting a form without navigating to a new page, often used for
comments or registration. AJAX significantly enhances the user experience by making
web applications feel more like desktop applications.

D. Backend Development (Node.js, Express.js,


Mongoose, RESTful APIs, JWT Authentication)
This section focuses on the backend technologies critical for building robust and scalable web
applications, as highlighted in the resume. It covers Node.js as a runtime, Express.js as a web
framework, Mongoose for MongoDB interaction, RESTful API design principles, and JWT for
secure authentication.

D.1. Node.js
Node.js is a JavaScript runtime environment built on Chrome's V8 JavaScript engine, designed
for building scalable network applications. Its architecture, particularly its single-threaded nature
and event-driven model, is a key area of understanding for developers.

D.1.1. What is Node.js and why is it single-threaded? How does it handle


concurrency?

Node.js is an open-source, cross-platform JavaScript runtime environment that allows


developers to execute JavaScript code outside of a web browser. It is specifically designed for
building highly scalable network applications, such as web servers, APIs, and real-time
applications.
Node.js is often described as single-threaded because its core execution model operates on a
single thread of execution for JavaScript code. This means that at any given time, only one
piece of JavaScript code is being executed. This design choice contributes to its efficiency and
simplicity, avoiding the complexities of multi-threading like deadlocks and race conditions.
Despite being single-threaded for JavaScript execution, Node.js is highly performant and
handles concurrency effectively through its event-driven, non-blocking I/O model and the
Event Loop.
●​ When Node.js encounters an I/O operation (like reading a file, making a network request,
or querying a database), it offloads these operations to the underlying operating system or
a thread pool (managed by libuv, a C++ library).
●​ Instead of waiting for these operations to complete (which would block the single thread),
Node.js registers a callback function and immediately moves on to execute the next
JavaScript code.
●​ Once the I/O operation completes, the operating system or thread pool notifies Node.js,
and the corresponding callback function is placed in a queue.
●​ The Event Loop continuously monitors this queue and pushes callbacks onto the call
stack when the main JavaScript thread is idle, allowing them to be executed. This
non-blocking, asynchronous approach enables Node.js to manage a large number of
concurrent connections and operations efficiently without creating a new thread for each
request, which would consume significant memory and resources.

D.1.2. Explain the Event Loop in Node.js.

The Event Loop is a crucial architectural component in Node.js that enables its non-blocking I/O
and asynchronous nature, allowing it to handle high concurrency with a single thread. It is a
continuous process that manages the execution of multiple chunks of code over time,
orchestrating callbacks and system events.
The Event Loop works by constantly checking two main components:
1.​ Call Stack: This is where synchronous JavaScript code is executed. When a function is
called, it is pushed onto the stack, and when it returns, it is popped off.
2.​ Callback Queue (or Message Queue): This queue holds asynchronous operations'
callback functions that are ready to be executed. These callbacks are pushed to the
queue by the Node.js runtime when an asynchronous operation (like a file read, network
request, or timer) completes.
The Event Loop's operation can be summarized as follows:
●​ It first executes all synchronous code in the call stack.
●​ Once the call stack is empty, it then checks the callback queue.
●​ If there are callbacks in the queue, it dequeues them one by one and pushes them onto
the call stack for execution.
●​ This process continues indefinitely, allowing Node.js to efficiently manage and process
asynchronous tasks without blocking the main thread. The Event Loop is what allows
Node.js to perform non-blocking I/O operations, making it suitable for applications that
require high throughput and low latency, such as real-time chat applications or API
services.

D.1.3. What is npm and package.json?

npm and package.json are integral parts of the Node.js ecosystem, essential for managing
project dependencies and metadata.
●​ npm (Node Package Manager): npm is the default package manager for JavaScript and
the world's largest software registry. It is primarily used to install, manage, and share
software libraries (packages or modules) in Node.js projects. Developers use npm
commands to:
○​ Install packages (e.g., npm install express).
○​ Manage project dependencies.
○​ Run scripts defined in package.json.
○​ Publish their own packages.
●​ package.json: This file is a manifest for a Node.js project. It is a JSON file located at the
root of a project directory that holds various metadata about the project. Its primary
purposes include:
○​ Project Metadata: Stores information like the project's name, version, description,
author, and license.
○​ Dependencies: Lists all external packages (libraries) that the project depends on.
These are typically categorized into:
■​ dependencies: Packages required for the application to run in a production
environment.
■​ devDependencies: Packages required only for development and testing (e.g.,
testing frameworks, build tools).
○​ Scripts: Defines custom scripts that can be run using npm run <script-name>,
automating common development tasks like starting the server, running tests, or
building the project.
○​ Entry Point: Specifies the main file of the application (e.g., main: "app.js"). The
package.json file is crucial for ensuring project reproducibility, dependency
management, and streamlining development workflows.
D.1.4. How do you handle exceptions in Node.js?

Effective exception handling is vital for building robust and reliable Node.js applications. Node.js
provides several mechanisms for managing errors and exceptions:
●​ try...catch Blocks: For synchronous code, try...catch blocks are the standard way to
handle exceptions. Code that might throw an error is placed within the try block, and if an
error occurs, it is caught by the catch block, allowing the application to gracefully recover
or log the error.
○​ Example:​
try {​
// Code that might throw an error​
const data = JSON.parse('invalid json');​
console.log(data);​
} catch (error) {​
console.error('An error occurred:', error.message);​
}​

●​ Asynchronous Error Handling (Callbacks, Promises, Async/Await): For


asynchronous operations, try...catch directly around the asynchronous call won't work for
errors thrown inside the callback.
○​ Callbacks: Errors are typically passed as the first argument to a callback function
(Node.js's "error-first" convention).
○​ Promises: Promises provide a structured way to handle success and failure. Errors
are caught using the .catch() method or a second argument to .then().
○​ async/await: When using async/await, try...catch blocks can be used around await
expressions, making asynchronous error handling resemble synchronous error
handling, which improves readability.
●​ process.on('uncaughtException'): This event listener can be used to catch exceptions
that were not handled by any try...catch block. While it can prevent the Node.js process
from crashing immediately, it is generally considered a last resort for logging and graceful
shutdown, not for regular error handling. It's important to note that after an
uncaughtException, the application state might be corrupted, so it's best to perform
cleanup and exit the process.
●​ process.on('unhandledRejection'): This event handles Promise rejections that are not
caught by a .catch() block. Similar to uncaughtException, it's a fallback for logging and
preventing process termination. Implementing a comprehensive error handling strategy,
including specific handlers for different types of errors and a global fallback, is crucial for
building resilient Node.js applications.

D.1.5. Explain async/await and Promises in JavaScript/Node.js.

async/await and Promises are modern JavaScript features (introduced in ES6 for Promises,
ES2017 for async/await) that provide powerful and readable ways to handle asynchronous
operations, significantly improving upon traditional callback-based approaches.
●​ Promises: A Promise in Node.js (and JavaScript) represents the eventual completion (or
failure) of an asynchronous operation and its resulting value. It acts as a placeholder for a
value that might not be available yet but will be in the future. A Promise can be in one of
three states:
○​ Pending: Initial state, neither fulfilled nor rejected.
○​ Fulfilled (Resolved): The operation completed successfully, and the Promise has a
resulting value.
○​ Rejected: The operation failed, and the Promise has a reason for the failure (an
error). Promises provide a structured way to chain asynchronous operations and
handle their success or failure, helping to mitigate "callback hell" (deeply nested
callbacks) by allowing you to attach .then() for success and .catch() for errors.
●​ async: The async keyword is used to declare a function as asynchronous. An async
function implicitly returns a Promise. If the function explicitly returns a non-Promise value,
that value will be automatically wrapped in a resolved Promise. If it throws an error, the
Promise will be rejected with that error.
○​ Example:​
async function fetchData() {​
return "Data fetched successfully!";​
}​
fetchData().then(data => console.log(data)); // Output: Data
fetched successfully!​

●​ await: The await keyword can only be used inside an async function. Its purpose is to
pause the execution of the async function until a Promise is resolved or rejected. When
await is placed before a Promise, the async function will wait for that Promise to settle. If
the Promise resolves, the await expression evaluates to the resolved value. If the Promise
rejects, the await expression throws the rejected value, which can then be caught by a
try...catch block within the async function.
○​ Example:​
async function getUserData(userId) {​
try {​
const response = await
fetch(`https://api.example.com/users/${userId}`);​
const data = await response.json();​
console.log(data);​
} catch (error) {​
console.error("Error fetching user data:", error);​
}​
}​
getUserData(123);​

In essence, async/await is syntactic sugar built on top of Promises. It allows developers to write
asynchronous code in a sequential, synchronous-looking manner, making it significantly easier
to read, write, and debug complex asynchronous flows compared to traditional callback
patterns.

D.1.6. Describe the difference between exports and module.exports in Node.js.

In Node.js, both exports and module.exports are used to define what a module exposes to other
modules when it is require()d. However, they are not interchangeable and understanding their
relationship is crucial.
●​ module.exports: This is the actual object that gets returned when a module is require()d.
By default, module.exports is an empty object {}. If you assign a value directly to
module.exports, that value will be the sole export of the module.
○​ Example: module.exports = { myFunc: () => {}, myVar: 10 }; (Exports an object)
○​ Example: module.exports = myFunction; (Exports a single function)
●​ exports: This is a convenience variable that is initially a reference to module.exports. It is
essentially a shortcut, allowing you to add properties to the module.exports object without
typing module.exports repeatedly.
○​ Example: exports.myFunc = () => {}; exports.myVar = 10; (Adds properties to the
default exported object)
The Key Difference and Common Pitfall: The critical point is that exports is just a reference to
module.exports. If you reassign exports to a new object (e.g., exports = {... };), you break this
reference. In such a case, exports will no longer point to the object that module.exports refers
to, and the module will still export the original module.exports object (which might be empty or
contain other properties) instead of what you intended to export via exports.
Therefore:
●​ If you want to export multiple items, you can add properties to exports (e.g., exports.name
= 'value';).
●​ If you want to export a single item (like a class, function, or a single object), you must
assign it directly to module.exports (e.g., module.exports = MyClass;).
●​ Never reassign exports itself if you intend for those changes to be reflected in
module.exports. Understanding this distinction prevents common module export errors in
Node.js.

D.1.7. Explain the concept of middleware in Express.js (in the context of Node.js).

Middleware functions are a fundamental concept in Express.js, a web application framework for
Node.js. They are functions that have access to the request object (req), the response object
(res), and the next middleware function in the application’s request-response cycle. Middleware
functions can perform various tasks:
●​ Execute any code.
●​ Make changes to the request and response objects.
●​ End the request-response cycle (by sending a response).
●​ Call the next middleware function in the stack.
Middleware functions are executed in the order they are defined. If a middleware function does
not end the request-response cycle, it must call next() to pass control to the next middleware
function; otherwise, the request will be left hanging.
Types of Middleware:
●​ Application-level middleware: Bound to an instance of the app object using app.use() or
app.METHOD().
○​ Example: A simple logging middleware:​
app.use((req, res, next) => {​
console.log(`${req.method} ${req.url} at ${new
Date().toISOString()}`);​
next(); // Pass control to the next handler​
});​
This middleware will log the HTTP method and URL for every incoming request.
●​ Router-level middleware: Functions that work the same way as application-level
middleware, but are bound to an instance of express.Router().
●​ Error-handling middleware: Functions that have four arguments: (err, req, res, next).
They are specifically designed to catch and process errors that occur during the
request-response cycle.
●​ Built-in middleware: Such as express.static for serving static files, express.json for
parsing JSON request bodies, and express.urlencoded for parsing URL-encoded request
bodies.
●​ Third-party middleware: Libraries like body-parser, cors, morgan (for logging), multer
(for file uploads) that provide additional functionalities. Middleware is essential for
structuring Express.js applications, allowing for modular and reusable code that handles
common concerns like authentication, logging, data parsing, and error handling.

D.1.8. What is the difference between setImmediate() and process.nextTick() in


Node.js?

setImmediate() and process.nextTick() are both functions in Node.js used to defer the execution
of a function, but they operate at different phases of the Event Loop, affecting their execution
order.
●​ process.nextTick(): This function defers the execution of a callback function until the
current phase of the event loop has completed, but before the event loop proceeds to the
next phase. process.nextTick() callbacks are executed very quickly, even before any
setImmediate() or setTimeout() callbacks. They are processed at the end of the current
operation, making them higher priority than other asynchronous operations. This is often
used for operations that need to run immediately after the current stack unwinds, but
before any I/O or timer events.
●​ setImmediate(): This function defers the execution of a callback function to the next
iteration of the event loop. setImmediate() callbacks are executed after all I/O events for
the current iteration have been processed, but before any setTimeout() callbacks that
might be due. It is designed to execute a script once the current poll phase completes.
Execution Order (simplified):
1.​ process.nextTick() callbacks (highest priority, within the current phase).
2.​ Promises (microtask queue, also high priority, after nextTick but before
setImmediate/setTimeout).
3.​ setTimeout() and setInterval() callbacks (after nextTick and Promises, based on their
timer).
4.​ I/O callbacks.
5.​ setImmediate() callbacks (after I/O callbacks, in the next iteration).
In summary, process.nextTick() runs immediately after the current operation finishes but before
the event loop moves to the next phase, while setImmediate() runs in the next iteration of the
event loop. This distinction is crucial for understanding the precise timing of asynchronous
operations in Node.js.

D.1.9. What are event emitters in Node.js?

Event emitters are a core concept in Node.js, providing a mechanism for raising and handling
custom events. Many of Node.js's built-in modules (like fs for file system, http for networking,
and stream for data streams) are instances of or inherit from the EventEmitter class.
The events module in Node.js provides the EventEmitter class, which allows objects to emit
named events that cause registered listener functions to be called. This implements the
observer pattern, promoting a decoupled and event-driven architecture.
Key methods of EventEmitter:
●​ on(eventName, listener) / addListener(eventName, listener): Registers a listener
function that will be called whenever the specified eventName is emitted.
●​ emit(eventName, [arg1], [arg2],...): Triggers the specified eventName, causing all
registered listeners for that event to be invoked. Any additional arguments passed to emit
are passed to the listener functions.
●​ removeListener(eventName, listener) / off(eventName, listener): Removes a
previously registered listener.
●​ once(eventName, listener): Registers a listener that will be invoked only once for the
specified eventName, after which it is automatically removed.
Event emitters are fundamental for building asynchronous and modular applications in Node.js,
allowing different parts of an application to communicate without direct dependencies.

D.1.10. What are streams in Node.js? Name some types.

Streams in Node.js are abstract interfaces for working with streaming data. They are a powerful
mechanism for handling large amounts of data efficiently, especially when the data is not
available all at once or when processing it in chunks is more memory-efficient. Streams allow
data to be read from a source or written to a destination in a continuous flow, rather than loading
the entire data into memory at once. This makes them ideal for handling large files, network
communication, and real-time data processing.
There are four fundamental types of streams in Node.js:
1.​ Readable Streams: Used for reading operations. Data is consumed from a source (e.g.,
a file, an incoming HTTP request, process.stdin).
○​ Example: fs.createReadStream(), http.IncomingMessage (on the server side).
2.​ Writable Streams: Used for writing operations. Data is written to a destination (e.g., a file,
an outgoing HTTP response, process.stdout).
○​ Example: fs.createWriteStream(), http.ServerResponse.
3.​ Duplex Streams: Streams that are both Readable and Writable. They can be used for
both reading from and writing to a single source/destination.
○​ Example: net.Socket (TCP sockets), zlib.Gzip (for compression/decompression).
4.​ Transform Streams: A type of Duplex stream where the output is computed based on
the input. Data written to a transform stream is processed (transformed) and then read
from the same stream.
○​ Example: zlib.createGzip() (compresses data), crypto.createCipher() (encrypts
data). Streams are crucial for optimizing performance and memory usage in
Node.js applications, especially when dealing with I/O-intensive tasks.

D.2. Express.js
Express.js is a minimal and flexible Node.js web application framework that provides a robust
set of features for web and mobile applications. It simplifies the process of building web
applications by offering a higher level of abstraction over raw Node.js HTTP modules.

D.2.1. What is Express.js and how does it differ from Node.js?


Node.js is a JavaScript runtime environment built on Chrome's V8 engine. Its primary purpose
is to allow JavaScript to be executed on the server-side, outside of a web browser. Node.js
provides core functionalities like file system access, networking, and process management, but
it's a low-level runtime that requires manual handling of HTTP requests, routing, and
middleware.
Express.js is a web application framework for Node.js. It is not a standalone language or
runtime but rather a layer built on top of Node.js's HTTP module. Express.js provides a robust
set of features and tools that simplify and streamline the development of web and mobile
applications and APIs.
The key differences are:
●​ Role: Node.js is the runtime environment that executes JavaScript code. Express.js is a
framework that provides tools and conventions for building web applications using
Node.js.
●​ Level of Abstraction: Node.js offers low-level control over HTTP. Express.js provides a
higher level of abstraction, simplifying tasks like routing, middleware integration, and
handling HTTP requests and responses.
●​ Purpose: Node.js is general-purpose for server-side JavaScript. Express.js is specifically
designed for building web applications and APIs. In essence, Node.js provides the
engine, and Express.js provides the car body and controls, making it much easier to drive
and build web applications.

D.2.2. How do you set up a basic Express.js server?

Setting up a basic Express.js server involves a few straightforward steps:


1.​ Initialize Project: Create a new directory for your project and initialize a Node.js project
within it by running npm init -y in your terminal. This creates a package.json file.
2.​ Install Express.js: Install the Express.js package as a dependency using npm: npm
install express.
3.​ Create Server File: Create a main JavaScript file (commonly app.js or index.js) in your
project directory.
4.​ Import Express and Create App Instance: In this file, import the express module and
create an instance of the Express application.​
const express = require('express');​
const app = express();​
const port = 3000; // Define the port number​

5.​ Define a Route: Set up a basic route to handle incoming HTTP requests. For example, a
GET request to the root path (/).​
app.get('/', (req, res) => {​
res.send('Hello, World!'); // Sends a "Hello, World!" response​
});​
This route defines what happens when a user accesses the root URL of your server. The
req object represents the HTTP request, and res represents the HTTP response.
6.​ Start the Server: Make the Express app listen for incoming requests on a specified port.​
app.listen(port, () => {​
console.log(`Server running on http://localhost:${port}`);​
});​
This minimal setup creates a functional web server that responds to requests on the defined
port.

D.2.3. Explain the concept of middleware in Express.js. Provide an example.

Middleware functions in Express.js are functions that execute in the middle of the
request-response cycle. They have access to the request object (req), the response object
(res), and a next() function, which is used to pass control to the next middleware function in the
stack. Middleware can perform various operations, such as executing code, modifying the
request and response objects, ending the request-response cycle, or calling the next
middleware.
Middleware functions are processed sequentially. If a middleware function does not send a
response to the client, it must call next() to hand off control to the next function; otherwise, the
request will hang, and the client will not receive a response.
Example: A simple logging middleware This middleware logs the HTTP method and URL of
every incoming request to the console.
const express = require('express');​
const app = express();​

// Define a logging middleware​
app.use((req, res, next) => {​
console.log(` ${req.method} ${req.url}`);​
next(); // Pass control to the next middleware or route handler​
});​

// Define a simple route​
app.get('/', (req, res) => {​
res.send('Welcome to the homepage!');​
});​

app.listen(3000, () => {​
console.log('Server running on port 3000');​
});​

When a request comes in, the logging middleware executes first, prints the request details, and
then calls next() to allow the request to proceed to the app.get('/') route handler. This
demonstrates how middleware can intercept and process requests before they reach their final
destination.

D.2.4. How do you handle routing in Express.js?

Routing in Express.js refers to determining how an application responds to a client request to a


particular endpoint, which is a URI (or path) and a specific HTTP method (GET, POST, PUT,
DELETE, etc.). Express.js provides robust mechanisms for defining routes, making it easy to
organize application logic based on different URLs and HTTP methods.
To handle routing:
1.​ Import Express and Create an App Instance: As in setting up a basic server.​
const express = require('express');​
const app = express();​

2.​ Define Route Handlers: Use methods corresponding to HTTP verbs (app.get(),
app.post(), app.put(), app.delete(), app.all()) to define routes. Each method takes a path
and one or more callback functions (route handlers) that are executed when the route is
matched.
○​ Simple Route:​
app.get('/hello', (req, res) => {​
res.send('Hello World!'); // Responds to GET requests on
/hello​
});​
This example defines a route that responds with "Hello World" when a GET request
is made to the /hello path.
○​ Handling Different HTTP Methods:​
app.post('/users', (req, res) => {​
// Logic to create a new user​
res.status(201).send('User created');​
});​

app.put('/users/:id', (req, res) => {​
const userId = req.params.id; // Access route parameters​
// Logic to update user with userId​
res.send(`User ${userId} updated`);​
});​

app.delete('/users/:id', (req, res) => {​
const userId = req.params.id;​
// Logic to delete user with userId​
res.send(`User ${userId} deleted`);​
});​

○​ Route Parameters: Express.js allows defining route parameters (e.g., :id in


/users/:id), which are named URL segments that capture values specified at their
position in the URL. These values are available in req.params.
○​ Query Parameters: Query parameters (e.g., ?name=value&age=20) are accessed
via req.query.
3.​ Using express.Router for Modular Routes: For larger applications, it's best practice to
organize routes into separate modules using express.Router.​
// users.js​
const express = require('express');​
const router = express.Router();​
router.get('/', (req, res) => res.send('List of users'));​
router.post('/', (req, res) => res.send('Create a user'));​
module.exports = router;​

// app.js​
const usersRouter = require('./users');​
app.use('/api/users', usersRouter); // Mount the router at
/api/users​

This modular approach keeps the main application file clean and makes routes easier to
manage and scale.

D.2.5. How can you serve static files in an Express.js application?

Serving static files (such as HTML, CSS, JavaScript, images, and other assets) in an Express.js
application is handled efficiently using the built-in express.static middleware. This middleware
function is specifically designed for serving static assets and is a crucial part of most web
applications.
To use express.static, you typically provide the name of the directory where your static files are
located. Express will then serve files directly from this directory.
Basic Usage: If your static files are in a folder named public at the root of your project, you
would configure Express as follows:
const express = require('express');​
const app = express();​

app.use(express.static('public'));​

// Now, files in 'public' can be accessed directly.​
// e.g., if 'public/css/style.css' exists, it can be accessed via
/css/style.css​
// if 'public/images/logo.png' exists, it can be accessed via
/images/logo.png​

app.get('/', (req, res) => {​
res.send('Check out the static files!');​
});​

app.listen(3000, () => {​
console.log('Server running on port 3000');​
});​

This setup makes all files within the public directory available under the root URL path.
Using a Virtual Path Prefix: You can also specify a virtual path prefix for your static files. This
means that the files will be served from the specified directory, but the URL path will start with
the virtual prefix.
app.use('/static', express.static('public'));​

// Now, 'public/css/style.css' would be accessed via
/static/css/style.css​
// 'public/images/logo.png' would be accessed via
/static/images/logo.png​

This is useful for organizing URLs or avoiding conflicts with other routes.
Configuring express.static Options: The express.static middleware can also be configured
with various options, such as maxAge for caching control.
app.use(express.static('public', {​
maxAge: '1d' // Caches static files for one day​
}));​

This option helps improve performance by allowing clients to cache static assets. The
express.static middleware is highly efficient for its purpose, but it's important to remember that
it's designed for static content and not for dynamic content or processing data from POST
requests.

D.2.6. How do you handle errors in Express.js? Write a custom error-handling


middleware.

Error handling in Express.js is typically managed through dedicated error-handling middleware


functions. Unlike regular middleware, error-handling middleware functions take four arguments:
(err, req, res, next). Express recognizes these four arguments as a signal that the function is an
error handler.
General Approach:
1.​ Catching Errors: Errors can be thrown synchronously within route handlers or
middleware, or passed to next() for asynchronous errors.
2.​ Error-Handling Middleware: Define one or more error-handling middleware functions at
the end of your middleware stack, after all other app.use() and route definitions. This
ensures that they catch errors from all preceding routes and middleware.
Custom Error-Handling Middleware Example:
const express = require('express');​
const app = express();​

// A simple route that might throw an error​
app.get('/broken', (req, res, next) => {​
const error = new Error('Something went wrong on the /broken
route!');​
error.status = 500; // Custom status code​
next(error); // Pass the error to the next middleware​
});​

app.get('/', (req, res) => {​
res.send('Homepage');​
});​

// Custom 404 Not Found middleware (should be placed before general
error handler)​
app.use((req, res, next) => {​
const error = new Error(`Not Found - ${req.originalUrl}`);​
res.status(404);​
next(error); // Pass the 404 error to the general error handler​
});​

// General error-handling middleware​
app.use((err, req, res, next) => {​
console.error(err.stack); // Log the error stack for debugging​
const statusCode = err.status |​

| 500; // Use custom status or default to 500​
res.status(statusCode).json({​
message: err.message,​
status: statusCode,​
// In​

Works cited

1. C Programming Interview Questions (2025) - GeeksforGeeks,


https://www.geeksforgeeks.org/c/c-interview-questions/ 2. Top C Programming Interview
Questions (2025) - InterviewBit, https://www.interviewbit.com/c-interview-questions/ 3. 60 C++
Interview Questions and Answers to Master Interview,
https://www.simplilearn.com/tutorials/cpp-tutorial/cpp-interview-questions 4. Top 20 Entry-Level
JavaScript Interview Questions | Boot.dev,
https://blog.boot.dev/javascript/javascript-interview-questions-entry-level/ 5. 110 Javascript
Interview Questions 2023 (with Answers) - JayDevs,
https://jaydevs.com/how-to-hire-js-developers/ 6. DOM scripting introduction - Learn web
development | MDN,
https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/DOM_scriptin
g 7. developer.mozilla.org,
https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Event_bubbli
ng#:~:text=Event%20capture,-An%20alternative%20form&text=This%20is%20like%20event%2
0bubbling,until%20the%20target%20is%20reached. 8. javascript - What is event bubbling and
capturing? - Stack Overflow,
https://stackoverflow.com/questions/4616694/what-is-event-bubbling-and-capturing 9. Bootstrap
Interview Questions - IT Training Institute in chennai - Credo Systemz,
https://www.credosystemz.com/bootstrap-interview-questions-and-answers/ 10. Top 30
Frequently Asked Ajax Interview Questions And Answers,
https://www.softwaretestinghelp.com/ajax-interview-questions/ 11. Node JS Developer Interview
Questions - Braintrust,
https://www.usebraintrust.com/hire/interview-questions/node-js-developers 12. Top 100+
Node.js Interview Questions and Answers for 2025 - Simplilearn.com,
https://www.simplilearn.com/tutorials/nodejs-tutorial/nodejs-interview-questions 13. Express.js
Interview Questions - Final Round AI,
https://www.finalroundai.com/blog/expressjs-interview-questions 14.
Devinterview-io/express-interview-questions: Express.js ... - GitHub,
https://github.com/Devinterview-io/express-interview-questions

You might also like