Functions and Arrow Functions
Master JavaScript functional programming. Understand parameters, standard functions, arrow function behavior, scopes, and rest parameters.
Downloaded from PrepIQ (https://prepiq.online)
Q1. What is the difference between function declaration and function expression?
| Aspect | Declaration | Expression |
|---|---|---|
| Syntax | function name() { } | const name = function() { } |
| Hoisting | Fully hoisted - can call before declaration | Not hoisted - throws error if called before |
| Named | Always named | Can be anonymous or named |
| When it runs | At parse time | At runtime |
// Function Declaration - hoisted
console.log(greet1()); // 'Hello' - works!
function greet1() {
return 'Hello';
}
// Function Expression - not hoisted
console.log(greet2()); // ReferenceError: Cannot access 'greet2' before initialization
const greet2 = function() {
return 'Hello';
};
// Named function expression
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};Interview tip: Explain that hoisting means function declarations are moved to the top of their scope at parse time, but function expressions are only initialized when the code runs. This is a common source of bugs.
Q2. What are arrow functions and how do they differ from regular functions?
Arrow functions (=>) are a concise syntax for writing functions introduced in ES6. They differ in syntax, this binding, and use cases.
| Feature | Regular Function | Arrow Function |
|---|---|---|
| Syntax | function name() { } | (params) => { } |
| this binding | Dynamic - depends on how called | Lexical - inherited from parent scope |
| arguments object | Yes, available | No, use rest parameters instead |
| Can be constructor | Yes, with new keyword | No, cannot use new |
| Implicit return | No, need return keyword | Yes, for single expression |
// Regular function
const add1 = function(a, b) {
return a + b;
};
// Arrow function - explicit return
const add2 = (a, b) => {
return a + b;
};
// Arrow function - implicit return (single expression)
const add3 = (a, b) => a + b;
// Single parameter (parens optional)
const double = x => x * 2;
// Arrow functions and this
const obj = {
name: 'John',
regular: function() {
console.log(this.name); // 'John' - this refers to obj
},
arrow: () => {
console.log(this); // Window/global - lexical this!
}
};
// Arrow functions have no arguments object
const test = () => {
console.log(arguments); // ReferenceError
};Interview tip: The most important difference is this binding. Arrow functions use lexical this from the enclosing scope, not dynamic this based on how they're called. This makes them unsuitable for object methods but perfect for callbacks and array methods.
Q3. What is the difference between parameters and arguments?
| Term | Meaning | Example |
|---|---|---|
| Parameters | Variables listed in function definition | function test(a, b) { } - a, b are parameters |
| Arguments | Actual values passed when calling the function | test(1, 2) - 1, 2 are arguments |
// Parameters are defined in the function signature
function greet(name, age) { // name, age = parameters
console.log(`Hello ${name}, you are ${age}`);
}
// Arguments are passed when calling
greet('John', 30); // 'John', 30 = arguments
// More arguments than parameters
greet('Jane', 25, 'Extra argument');
// The extra argument is ignored (unless using rest parameters)
// Fewer arguments than parameters
greet('Bob');
// age will be undefined
// Using the arguments object (only in regular functions)
function test() {
console.log(arguments); // [1, 2, 3]
console.log(arguments[0]); // 1
console.log(arguments.length); // 3
}
test(1, 2, 3);
// Arrow functions don't have arguments object
const arrow = () => {
console.log(arguments); // ReferenceError: arguments is not defined
};Interview tip: Explain that you can pass any number of arguments to a function in JavaScript, even if the number of parameters doesn't match. Use rest parameters (...args) to capture excess arguments.
Q4. What are default parameters and how do you use them?
Default parameters allow you to provide default values for function parameters if no argument is passed. This was introduced in ES6.
// Before ES6 - using || operator (problematic)
function greet(name) {
name = name || 'Guest'; // 0, false, '' also trigger default
console.log('Hello ' + name);
}
// ES6 default parameters (correct approach)
function greet2(name = 'Guest') {
console.log('Hello ' + name);
}
greet2(); // 'Hello Guest'
greet2('John'); // 'Hello John'
greet2(null); // 'Hello null' - null is a value!
greet2(undefined); // 'Hello Guest' - undefined triggers default
// Arrow functions with defaults
const add = (a = 0, b = 0) => a + b;
add(); // 0
add(5); // 5
add(5, 3); // 8
// Default parameters can reference other parameters
function createUser(name = 'Anonymous', email = name + '@example.com') {
return { name, email };
}
createUser('John'); // { name: 'John', email: 'John@example.com' }
createUser('Jane', 'jane@gmail.com'); // { name: 'Jane', email: 'jane@gmail.com' }
// Default parameters can be expressions
function config(timeout = 5000, retries = Math.floor(timeout / 1000)) {
return { timeout, retries };
}
config(); // { timeout: 5000, retries: 5 }
// Default parameters can be function calls
function test(value = getDefaultValue()) {
console.log(value);
}
// Skipping parameters (use undefined)
function multiply(a = 2, b = 3, c = 4) {
return a * b * c;
}
multiply(undefined, 5); // 2 * 5 * 4 = 40 (a uses default)Interview tip: Remember that null is a value and won't trigger the default, but undefined will. Also, default parameters are evaluated at call time, not at function definition time.
Q5. What is a higher-order function?
A higher-order function is a function that either takes one or more functions as arguments, or returns a function. This is a fundamental concept in functional programming.
// Higher-order function that takes a function as argument
function map(arr, fn) {
const result = [];
for (let i = 0; i < arr.length; i++) {
result.push(fn(arr[i]));
}
return result;
}
const nums = [1, 2, 3, 4];
const doubled = map(nums, x => x * 2);
console.log(doubled); // [2, 4, 6, 8]
// Higher-order function that returns a function
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Real-world examples from JavaScript
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
// Array.prototype.filter is a higher-order function
const over30 = users.filter(user => user.age > 30);
// Array.prototype.map is a higher-order function
const names = users.map(user => user.name);
// Array.prototype.reduce is a higher-order function
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
// Composing higher-order functions
function compose(f, g) {
return x => f(g(x));
}
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const add5ThenMultiply2 = compose(multiply2, add5);
console.log(add5ThenMultiply2(3)); // (3 + 5) * 2 = 16Interview tip: Emphasize that higher-order functions enable functional programming patterns like map, filter, reduce. They're also key to decorators, middleware, and partial application. Mention that JavaScript's first-class functions make this possible.
Q6. What is function currying and what are its benefits?
Currying is a technique where a function that takes multiple arguments is transformed into a sequence of functions that each take a single argument. This enables partial application and function reuse.
// Regular function
function add(a, b, c) {
return a + b + c;
}
console.log(add(1, 2, 3)); // 6
// Curried version - manual
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(curriedAdd(1)(2)(3)); // 6
// Curried version - using arrow functions
const curriedAdd2 = a => b => c => a + b + c;
console.log(curriedAdd2(1)(2)(3)); // 6
// Partial application - a key benefit of currying
const add1 = curriedAdd2(1);
const add1And2 = add1(2);
const result = add1And2(3); // 6
// Or more concisely
const add5ThenAddMore = curriedAdd2(5);
console.log(add5ThenAddMore(3)(2)); // 10
// Real-world example: creating specialized functions
const multiply = (a) => (b) => a * b;
const double = multiply(2);
const triple = multiply(3);
const quadruple = multiply(4);
console.log(double(5)); // 10
console.log(triple(5)); // 15
console.log(quadruple(5)); // 20
// Curry helper function
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return (...nextArgs) => curried(...args, ...nextArgs);
}
};
}
const curriedSum = curry((a, b, c) => a + b + c);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6 - can still use multiple argsInterview tip: Explain that currying is mainly used for partial application (creating specialized functions from general ones) and function composition. It improves code reusability and readability in functional programming.
Q7. What are rest parameters and the spread operator?
Rest parameters (...) allow you to collect multiple arguments into an array. The spread operator (...) does the opposite - it expands an array or object into individual elements.
// Rest parameters - collect into array
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// Rest with named parameters
function greet(greeting, ...names) {
console.log(greeting + ' ' + names.join(', '));
}
greet('Hello', 'John', 'Jane', 'Bob'); // 'Hello John, Jane, Bob'
// Spread with arrays
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const max = Math.max(...[1, 5, 3, 2]); // equivalent to Math.max(1, 5, 3, 2)
console.log(max); // 5
// Spread with objects
const user1 = { name: 'John', age: 30 };
const user2 = { ...user1, email: 'john@gmail.com' };
// { name: 'John', age: 30, email: 'john@gmail.com' }
// Spread to merge objects
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 }; // b will be overwritten
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
// Rest in arrow functions
const multiply = (...nums) => nums.reduce((a, b) => a * b, 1);
console.log(multiply(2, 3, 4)); // 24
// Difference: Rest collects, Spread expands
const nums = [1, 2, 3];
// Spread: expand array
function test(a, b, c) {
console.log(a, b, c);
}
test(...nums); // 1, 2, 3
// Rest: collect arguments
function collect(...args) {
console.log(args); // [1, 2, 3]
}
collect(1, 2, 3);Interview tip: Rest parameters must be the last parameter in the function signature. Spread operator can be used with arrays, objects, function calls, and function parameters. They're ES6 features that enable more flexible code.
Q8. What is function composition and how does it work?
Function composition is the process of combining multiple functions to produce a new function. The output of one function becomes the input of the next.
// Basic function composition
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const subtract1 = x => x - 1;
// Without composition - nested calls
const result1 = subtract1(multiply2(add5(10)));
console.log(result1); // ((10 + 5) * 2) - 1 = 29
// With composition - using a compose helper
const compose = (f, g) => x => f(g(x));
const composedFunc = compose(subtract1, compose(multiply2, add5));
console.log(composedFunc(10)); // 29
// Compose multiple functions
const composeMany = (...fns) => x => fns.reduceRight((val, fn) => fn(val), x);
const pipeline = composeMany(subtract1, multiply2, add5);
console.log(pipeline(10)); // 29
// Pipe (left to right) vs Compose (right to left)
const pipe = (...fns) => x => fns.reduce((val, fn) => fn(val), x);
const leftToRight = pipe(add5, multiply2, subtract1);
console.log(leftToRight(10)); // (10 + 5) * 2 - 1 = 29
// Real-world example: string processing
const trim = str => str.trim();
const uppercase = str => str.toUpperCase();
const reverse = str => str.split('').reverse().join('');
const addExclamation = str => str + '!';
const processString = composeMany(addExclamation, reverse, uppercase, trim);
console.log(processString(' hello world ')); // '!DLROW OLLEHT'
// Composition with pure functions
const getUser = (id) => ({ id, name: 'John', email: 'john@gmail.com' });
const getEmail = user => user.email;
const toLowerCase = str => str.toLowerCase();
const extractDomain = email => email.split('@')[1];
const getUserDomain = composeMany(extractDomain, toLowerCase, getEmail);
console.log(getUserDomain(1)); // 'gmail.com'Interview tip: Function composition is a core concept in functional programming. Explain the difference between compose (right to left) and pipe (left to right). Mention that composition requires pure functions for predictable behavior and improves code reusability.
Q9. What are IIFE (Immediately Invoked Function Expressions)?
An IIFE is a function that is defined and immediately executed at the same time. It's a design pattern that creates a new function scope to avoid polluting the global namespace.
// Basic IIFE
(function() {
console.log('This runs immediately!');
})();
// IIFE with return value
const result = (function() {
return 'Hello from IIFE';
})();
console.log(result); // 'Hello from IIFE'
// IIFE with parameters
(function(name, age) {
console.log(`Hello ${name}, you are ${age}`);
})('John', 30); // 'Hello John, you are 30'
// IIFE with arrow functions
(() => {
console.log('Arrow function IIFE');
})();
// Create private scope - avoid global namespace pollution
const myModule = (function() {
let private = 'secret'; // private variable
return {
getSecret: () => private,
setSecret: (newSecret) => { private = newSecret; },
publicMethod: () => console.log('Public method')
};
})();
console.log(myModule.private); // undefined - private is protected
console.log(myModule.getSecret()); // 'secret'
myModule.setSecret('new secret');
// IIFE with immediately invoked result
const add = (function() {
const a = 10;
const b = 20;
return a + b;
})();
console.log(add); // 30
// Module pattern using IIFE
const Calculator = (function() {
// Private variable
let result = 0;
// Private method
const log = (value) => console.log(value);
return {
add: (x) => { result += x; log(result); return this; },
subtract: (x) => { result -= x; log(result); return this; },
multiply: (x) => { result *= x; log(result); return this; },
getResult: () => result
};
})();
Calculator.add(5); // 5
Calculator.add(3); // 8
Calculator.multiply(2); // 16
console.log(Calculator.getResult()); // 16Interview tip: Explain that IIFE is especially useful for the module pattern to create private variables and methods. Before ES6 modules were popular, IIFE was the primary way to avoid global namespace pollution. Mention modern alternatives like ES6 modules, but emphasize that IIFEs are still relevant and used in many codebases.
Q10. What is recursion and when is it useful?
Recursion is a programming technique where a function calls itself to solve a problem by breaking it down into smaller subproblems. The function must have a base case to stop recursion.
// Factorial - classic recursion example
function factorial(n) {
// Base case - stops recursion
if (n <= 1) return 1;
// Recursive case - function calls itself
return n * factorial(n - 1);
}
console.log(factorial(5)); // 5 * 4 * 3 * 2 * 1 = 120
// Fibonacci sequence
function fib(n) {
// Base cases
if (n <= 1) return n;
// Recursive case
return fib(n - 1) + fib(n - 2);
}
console.log(fib(6)); // 8
// Tree traversal - very useful for recursion
const tree = {
value: 1,
left: {
value: 2,
left: { value: 4 },
right: { value: 5 }
},
right: {
value: 3,
left: { value: 6 },
right: { value: 7 }
}
};
function traverse(node) {
if (!node) return;
console.log(node.value);
traverse(node.left);
traverse(node.right);
}
traverse(tree); // 1, 2, 4, 5, 3, 6, 7
// Problem with recursion: Performance
// fib(40) will be VERY slow because of repeated calculations
// Solution 1: Memoization (caching)
function fibMemo(n, memo = {}) {
if (memo[n]) return memo[n];
if (n <= 1) return n;
memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
return memo[n];
}
console.log(fibMemo(40)); // Much faster!
// Solution 2: Dynamic Programming (bottom-up)
function fibDP(n) {
if (n <= 1) return n;
const dp = [0, 1];
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// Deep copy using recursion
function deepCopy(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) {
return obj.map(item => deepCopy(item));
}
const copy = {};
for (let key in obj) {
copy[key] = deepCopy(obj[key]);
}
return copy;
}Interview tip: Always mention the base case when explaining recursion. Discuss the performance implications (stack overflow on deep recursion) and mention optimization techniques like memoization or converting to iteration. Explain when recursion is natural and elegant (tree/graph traversal) versus when iteration is better (linear problems).
Functions and Arrow Functions
Master JavaScript functional programming. Understand parameters, standard functions, arrow function behavior, scopes, and rest parameters.
What is the difference between function declaration and function expression?
AspectDeclarationExpressionSyntaxfunction name() { }const name = function() { }HoistingFully hoisted - can cal...
What are arrow functions and how do they differ from regular functions?
Arrow functions (=>) are a concise syntax for writing functions introduced in ES6. They differ in syntax, this...
What is the difference between parameters and arguments?
TermMeaningExampleParametersVariables listed in function definitionfunction test(a, b) { } - a, b are paramete...
What are default parameters and how do you use them?
Default parameters allow you to provide default values for function parameters if no argument is passed. This...
What is a higher-order function?
A higher-order function is a function that either takes one or more functions as arguments, or returns a funct...
What is function currying and what are its benefits?
Currying is a technique where a function that takes multiple arguments is transformed into a sequence of funct...
What are rest parameters and the spread operator?
Rest parameters (...) allow you to collect multiple arguments into an array. The spread operator (...) does th...
What is function composition and how does it work?
Function composition is the process of combining multiple functions to produce a new function. The output of o...
What are IIFE (Immediately Invoked Function Expressions)?
An IIFE is a function that is defined and immediately executed at the same time. It's a design pattern that cr...
What is recursion and when is it useful?
Recursion is a programming technique where a function calls itself to solve a problem by breaking it down into...