Scripting LanguageUnit 314 min read
JavaScript Functions & Methods: Definitions, Scope, Closures, Arrow Functions & Built-in Methods
Unit 3 of Scripting Language covers JavaScript functions (declaration, expression, parameters, rest/spread, recursion), scope (global, local, block, function), closures, arrow functions, and built-in methods (map, filter, reduce, forEach). Includes real-world examples from eSewa, Daraz, and Ncell, plus visual traces of
TAKEAWAYS
- JavaScript functions are first-class citizens: they can be passed as arguments, returned from other functions, and assigned to variables.
- Scope rules (global, local, block, function) determine variable accessibility, with
let/constenforcing block scope. - Closures capture the lexical environment of their creation, enabling data privacy and function factories.
- Arrow functions (
=>) are concise but lackthisbinding andargumentsobject, making them unsuitable for object methods or callbacks needing dynamic context. - Built-in array methods like
map,filter, andreducetransform, filter, and aggregate data without mutating the original array. - Recursion is a powerful technique for problems with self-similar subproblems (e.g., tree traversals, factorial calculation).
1. JavaScript Functions: Basics and Types
JavaScript functions are reusable blocks of code that perform a specific task. They can be defined in multiple ways and have unique behaviors like first-class functions and closures.
1.1 Function Declaration vs. Function Expression
flowchart TD
A["Function Declaration"] -->|"Syntax"| B["function name(params) { ... }"]
C["Function Expression"] -->|"Syntax"| D["const name = function(params) { ... }"]
E["Arrow Function"] -->|"Syntax"| F["const name = (params) => { ... }"]
B -->|"Hoisted"| G["Can be called before declaration"]
D -->|"Not Hoisted"| H["Must be defined before use"]
F -->|"Lexical `this`"| I["No `this` binding; no `arguments`"]Example: Function Declaration vs. Expression
// Declaration (hoisted)
function add(a, b) {
return a + b;
}
// Expression (not hoisted)
const subtract = function(a, b) {
return a - b;
};
// Arrow function (lexical `this`)
const multiply = (a, b) => a * b;
1.2 Parameters and Arguments
- Parameters: Variables listed in the function definition.
- Arguments: Actual values passed when calling the function.
- Rest Parameters (
...args): Collects all arguments into an array. - Default Parameters: Assign default values if arguments are missing.
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
// Default parameters
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
Trace: sum(1, 2, 3) Execution
| Step | numbers (array) |
reduce accumulator |
Result |
|---|---|---|---|
| 1 | [1, 2, 3] |
1 (initial) |
1 |
| 2 | [2, 3] |
1 + 2 = 3 |
3 |
| 3 | [3] |
3 + 3 = 6 |
6 |
| 4 | [] |
6 + 3 = 9 |
9 |
2. Scope in JavaScript
Scope determines where variables are accessible. JavaScript has global, local (function), and block scope.
2.1 Scope Rules Visualized
Example: Scope in Action
var globalVar = "I'm global";
let blockVar = "I'm block-scoped";
function scopeTest() {
var functionVar = "I'm function-scoped";
if (true) {
let blockVar = "I'm block-scoped inside!";
console.log(blockVar); // "I'm block-scoped inside!"
}
console.log(globalVar); // "I'm global"
console.log(functionVar); // "I'm function-scoped"
// console.log(blockVar); // ReferenceError (block scope)
}
Key Differences
| Scope Type | Declaration Keyword | Lifetime | Accessibility |
|---|---|---|---|
| Global | var/let/const |
Entire script | Everywhere |
| Function | var/let/const |
Inside function | Only within function |
| Block | let/const |
Inside {} |
Only within block |
3. Closures: The Power of Lexical Scoping
A closure is a function that retains access to its lexical scope even after the outer function has executed.
3.1 How Closures Work
sequenceDiagram
participant User
participant outerFunc
participant innerFunc
User->>outerFunc: Call outerFunc(x)
outerFunc->>innerFunc: Return innerFunc (captures x)
User->>innerFunc: Call innerFunc()
innerFunc->>User: Returns x (lexical scope preserved)Example: Counter with Closure
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Trace: Closure Execution
| Step | count (outer scope) |
innerFunc return |
Output |
|---|---|---|---|
| 1 | 0 (initial) |
count++ → 1 |
1 |
| 2 | 1 (retained) |
count++ → 2 |
2 |
3.2 Real-World Use: Data Privacy in eSewa
eSewa uses closures to hide sensitive user data (e.g., API keys) from global scope:
function secureAPICall(apiKey) {
return function(endpoint, data) {
// apiKey is not exposed globally
fetch(`https://api.esewa.com/${endpoint}`, {
method: "POST",
headers: { "Authorization": `Bearer ${apiKey}` },
body: JSON.stringify(data)
});
};
}
const callEsewa = secureAPICall("hidden_api_key_123");
callEsewa("pay", { amount: 500 });
4. Arrow Functions: Concise Syntax with Caveats
Arrow functions (=>) provide a shorter syntax but do not bind their own this or have an arguments object.
4.1 Arrow Function Syntax
flowchart LR
A["Single Param"] -->|"Syntax"| B["(param) => { ... }"]
C["Multiple Params"] -->|"Syntax"| D["(a, b) => a + b"]
E["No Params"] -->|"Syntax"| F["() => 'Hello'"]
G["Implicit Return"] -->|"Syntax"| H["(a) => a * 2"]Example: Arrow Function vs. Regular Function
// Regular function (has `this` and `arguments`)
function regularFunc() {
console.log(this); // Window (global) or object context
console.log(arguments); // [1, 2, 3]
}
// Arrow function (lexical `this`)
const arrowFunc = () => {
console.log(this); // Inherits `this` from parent scope
// console.log(arguments); // ReferenceError
};
Trace: this Binding in Objects
const obj = {
name: "Daraz",
greet: function() {
console.log(`Hello, ${this.name}!`);
},
greetArrow: () => {
console.log(`Hello, ${this.name}!`); // `this` is Window (or undefined in strict mode)
}
};
obj.greet(); // "Hello, Daraz!"
obj.greetArrow(); // Error (or "Hello, undefined!")
5. Built-in Array Methods
JavaScript arrays have powerful methods for iteration, transformation, and aggregation without mutating the original array.
5.1 Common Array Methods
| Method | Purpose | Example | Mutates Original? |
|---|---|---|---|
map |
Transform each element | [1, 2].map(x => x * 2) → [2, 4] |
❌ No |
filter |
Return elements meeting condition | [1, 2, 3].filter(x => x > 1) → [2, 3] |
❌ No |
reduce |
Aggregate values into one result | [1, 2, 3].reduce((a, b) => a + b, 0) → 6 |
❌ No |
forEach |
Execute a function for each item | [1, 2].forEach(console.log) |
❌ No |
find |
Return first matching element | [1, 2, 3].find(x => x > 1) → 2 |
❌ No |
5.2 Real-World Use: Daraz Order Processing
Daraz uses reduce to calculate total order value from items:
const orderItems = [
{ product: "Laptop", price: 50000, quantity: 1 },
{ product: "Mouse", price: 500, quantity: 2 }
];
const total = orderItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
console.log(`Total: Rs. ${total}`); // Rs. 50500
Trace: reduce Execution
| Step | sum (accumulator) |
item (current) |
sum + (item.price * item.quantity) |
Result |
|---|---|---|---|---|
| 1 | 0 (initial) |
{ product: "Laptop", price: 50000, quantity: 1 } |
0 + (50000 * 1) = 50000 |
50000 |
| 2 | 50000 |
{ product: "Mouse", price: 500, quantity: 2 } |
50000 + (500 * 2) = 50500 |
50500 |
5.3 map vs. forEach
| Feature | map |
forEach |
|---|---|---|
| Purpose | Transform and return new array | Execute side effects |
| Return | New array | undefined |
| Use Case | Data transformation | Logging, DOM updates |
| Example | [1, 2].map(x => x * 2) → [2, 4] |
[1, 2].forEach(console.log) |
Example: Converting Ncell Users to Objects
const userIds = ["user1", "user2", "user3"];
// Using map (returns new array)
const users = userIds.map(id => ({ id, status: "active" }));
console.log(users);
// Output: [{ id: "user1", status: "active" }, ...]
// Using forEach (no return)
userIds.forEach(id => console.log(`Processing ${id}`));
// Output: "Processing user1", "Processing user2", etc.
6. Recursion: Solving Problems with Self-Calls
Recursion is a technique where a function calls itself to solve smaller instances of the same problem.
6.1 Recursive Factorial Example
flowchart TD
A["factorial(5)"] -->|"Returns"| B["5 * factorial(4)"]
B -->|"Returns"| C["4 * factorial(3)"]
C -->|"Returns"| D["3 * factorial(2)"]
D -->|"Returns"| E["2 * factorial(1)"]
E -->|"Returns"| F["1 * factorial(0)"]
F -->|"Base Case"| G["1"]
G -->|"Unwind"| E["2 * 1 = 2"]
E -->|"Unwind"| D["3 * 2 = 6"]
D -->|"Unwind"| C["4 * 6 = 24"]
C -->|"Unwind"| B["5 * 24 = 120"]
B -->|"Final"| A["120"]Code: Recursive Factorial
function factorial(n) {
if (n === 0) return 1; // Base case
return n * factorial(n - 1); // Recursive case
}
console.log(factorial(5)); // 120
Trace: factorial(3) Execution
| Step | Call Stack | Return Value | Result |
|---|---|---|---|
| 1 | factorial(3) |
3 * ? |
- |
| 2 | factorial(2) |
2 * ? |
- |
| 3 | factorial(1) |
1 * ? |
- |
| 4 | factorial(0) → 1 |
1 |
Unwind |
| 5 | 1 * 1 = 1 |
1 |
Unwind |
| 6 | 2 * 1 = 2 |
2 |
Unwind |
| 7 | 3 * 2 = 6 |
6 | Output |
6.2 Real-World Use: Ncell Call Tree
Ncell uses recursion to flatten nested call logs for billing:
function flattenCalls(callLog) {
let result = [];
for (const call of callLog) {
if (Array.isArray(call)) {
result.push(...flattenCalls(call)); // Recursive flatten
} else {
result.push(call);
}
}
return result;
}
const nestedCalls = ["call1", ["call2", ["call3"]]];
console.log(flattenCalls(nestedCalls)); // ["call1", "call2", "call3"]
7. Method Overriding in JavaScript (Prototype Chaining)
JavaScript uses prototype-based inheritance, where child objects can override parent methods.
7.1 Prototype Chain Visualization
classDiagram
class Animal {
+speak() "Makes a sound"
}
class Dog {
+speak() "Barks!"
}
Animal <|-- Dog : InheritsExample: Overriding speak()
class Animal {
speak() {
return "Some sound";
}
}
class Dog extends Animal {
speak() { // Overrides parent method
return "Bark!";
}
}
const dog = new Dog();
console.log(dog.speak()); // "Bark!"
Trace: dog.speak() Execution
| Step | Method Lookup | Result |
|---|---|---|
| 1 | Check Dog.prototype |
Finds speak() → "Bark!" |
| 2 | (No need to check Animal) |
- |
In the Real World
eSewa Payment API
- Closures hide API keys from global scope, preventing exposure in client-side code.
- Example: The
secureAPICallfunction (Section 3.2) ensures sensitive data is never leaked.
Daraz Order Processing
reducemethod calculates total order value dynamically (Section 5.2).- Example: Summing
price * quantityfor all items in a cart.
Ncell Call Logs
- Recursion flattens nested call logs for billing reports (Section 6.2).
- Example: Converting
["call1", ["call2", ["call3"]]]into["call1", "call2", "call3"].
Khalti Transaction Validation
- Arrow functions are used in event handlers for concise syntax (e.g., button clicks).
- Example:
document.getElementById("payBtn").addEventListener("click", () => { validatePayment(); // Arrow function preserves `this` });
NEPSE Stock Data Aggregation
mapandfiltertransform raw stock data into readable formats.- Example:
const stocks = [{ symbol: "NTC", price: 100 }, { symbol: "NEPSE", price: 200 }]; const highValueStocks = stocks.filter(s => s.price > 150).map(s => s.symbol); // ["NEPSE"]
Exam Tip
Function Questions
- Always declare functions before calling them if using declarations (hoisting).
- For
sum()with arbitrary parameters, use rest parameters (...args). - Example answer for past exam:
function sum(...numbers) { return numbers.reduce((total, num) => total + num, 0); } console.log(sum(1, 2)); // 3
Scope and Closures
- Use
let/constfor block scope to avoid hoisting issues. - Closures are tested via data privacy or function factories. Memorize the
createCounterpattern.
- Use
Array Methods
map→ Transform → Returns new array.filter→ Select → Returns filtered array.reduce→ Aggregate → Returns single value.forEach→ Side effects → Returnsundefined.- Never mutate the original array in exams unless asked.
Recursion
- Always include a base case to terminate recursion.
- Trace the call stack step-by-step (like the
factorialexample).
Arrow Functions
- Use them for concise callbacks but avoid in object methods needing
this. - Example pitfall:
const obj = { name: "Pathao", greet: () => console.log(this.name) // ❌ `this` is Window! };
- Use them for concise callbacks but avoid in object methods needing
Prototype Overriding
- Child classes override parent methods by redefining them.
- Use
extendsandsuper()if needed (though TU syllabus focuses on basic overriding).
Pro Tip for TU Exams:
- Draw diagrams for scope/closure questions (e.g., lexical environment boxes).
- Trace variable values in tables for recursion or
reduceoperations. - Compare methods in tables (e.g.,
mapvs.forEach) to show differences clearly.
Based on the TU BCA syllabus for Scripting Language (CACS254), unit 3.
Discussion
Loading…