Functions & Scope Skill
Quick Reference Card
Function Styles
javascript
1// Declaration (hoisted)
2function greet(name) { return `Hello, ${name}!`; }
3
4// Expression (not hoisted)
5const greet = function(name) { return `Hello, ${name}!`; };
6
7// Arrow (lexical this)
8const greet = (name) => `Hello, ${name}!`;
9const greet = name => `Hello, ${name}!`; // Single param
10const getUser = async (id) => await fetch(`/api/${id}`);
Scope Rules
Global Scope
└── Function Scope
└── Block Scope (let/const)
javascript
1const global = 'accessible everywhere';
2
3function outer() {
4 const outerVar = 'accessible in outer + inner';
5
6 function inner() {
7 const innerVar = 'only accessible here';
8 console.log(global, outerVar, innerVar); // All work
9 }
10}
Closure Pattern
javascript
1function createCounter() {
2 let count = 0; // Private state
3
4 return {
5 increment: () => ++count,
6 decrement: () => --count,
7 get: () => count
8 };
9}
10
11const counter = createCounter();
12counter.increment(); // 1
13counter.increment(); // 2
This Binding Rules
| Context | this Value |
|---|
| Global | window/global |
| Object method | The object |
| Arrow function | Lexical (outer) |
call/apply/bind | Explicit value |
Constructor (new) | New instance |
javascript
1// Explicit binding
2fn.call(thisArg, arg1, arg2);
3fn.apply(thisArg, [args]);
4const bound = fn.bind(thisArg);
Advanced Patterns
javascript
1// IIFE (Immediately Invoked)
2const module = (function() {
3 const private = 'hidden';
4 return { getPrivate: () => private };
5})();
6
7// Currying
8const multiply = a => b => a * b;
9const double = multiply(2);
10double(5); // 10
11
12// Memoization
13function memoize(fn) {
14 const cache = new Map();
15 return (...args) => {
16 const key = JSON.stringify(args);
17 if (!cache.has(key)) cache.set(key, fn(...args));
18 return cache.get(key);
19 };
20}
Troubleshooting
Common Issues
| Problem | Symptom | Fix |
|---|
Lost this | undefined or wrong value | Use arrow fn or .bind() |
| Closure loop bug | All callbacks same value | Use let not var |
| Hoisting confusion | Undefined before declaration | Declare at top |
| TDZ error | ReferenceError | Move let/const before use |
The Classic Loop Bug
javascript
1// BUG: var is function-scoped
2for (var i = 0; i < 3; i++) {
3 setTimeout(() => console.log(i), 100);
4}
5// Output: 3, 3, 3
6
7// FIX: Use let (block-scoped)
8for (let i = 0; i < 3; i++) {
9 setTimeout(() => console.log(i), 100);
10}
11// Output: 0, 1, 2
Debug Checklist
javascript
1// 1. Check this context
2console.log('this is:', this);
3
4// 2. Verify closure captures
5function test() {
6 let x = 1;
7 return () => { console.log('x:', x); };
8}
9
10// 3. Check hoisting
11console.log(typeof myFunc); // 'function' or 'undefined'?
Production Patterns
Factory Pattern
javascript
1function createLogger(prefix) {
2 return {
3 log: (msg) => console.log(`[${prefix}] ${msg}`),
4 error: (msg) => console.error(`[${prefix}] ${msg}`)
5 };
6}
7
8const apiLogger = createLogger('API');
9apiLogger.log('Request received');
Debounce/Throttle
javascript
1function debounce(fn, delay) {
2 let timeoutId;
3 return (...args) => {
4 clearTimeout(timeoutId);
5 timeoutId = setTimeout(() => fn(...args), delay);
6 };
7}
Related
- Agent 02: Functions & Scope (detailed learning)
- Skill: fundamentals: Variables and basics
- Skill: asynchronous: Async functions