JavaScript Functions | Declarations, Arrows, Callbacks, and Closures
이 글의 핵심
Hands-on guide to JavaScript functions: how to define them, pass data, use closures, and avoid common this pitfalls.
Introduction
What is a function?
A function is a block of code that performs a task. In JavaScript, functions are first-class values: you can assign them to variables, pass them as arguments, and return them from other functions.
1. Ways to define functions
Function declaration
function add(a, b) {
return a + b;
}
console.log(add(10, 20)); // 30
greet(); // hoisted
function greet() {
console.log("Hello!");
}
Function expression
const subtract = function(a, b) {
return a - b;
};
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1);
};
// multiply(); // ReferenceError if called before assignment
const multiply = function(a, b) {
return a * b;
};
Arrow functions (ES6+)
const add = (a, b) => {
return a + b;
};
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => console.log("Hello!");
const makePerson = (name, age) => ({ name, age });
console.log(makePerson("Alice", 25));
const complexFunc = (a, b) => {
const sum = a + b;
const product = a * b;
return { sum, product };
};
Arrow function notes:
- Concise syntax
- Lexical
this(inherits from enclosing scope) - No
argumentsobject—use rest parameters (...args) - Not constructible—cannot use
new
When to use them: array callbacks, short utilities, when lexical this is what you want.
Comparison
| Declaration | Expression | Arrow | |
|---|---|---|---|
| Hoisting | Yes | No* | No* |
this | dynamic | dynamic | lexical |
arguments | yes | yes | no |
new | yes | yes | no |
* Assignments are not hoisted like declarations.
2. Parameters and return values
Default parameters
function greet(name = "guest") {
return `Hello, ${name}!`;
}
function createArray(length = 10, fill = 0) {
return Array(length).fill(fill);
}
Rest parameters
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
function introduce(greeting, ...names) {
return `${greeting}, ${names.join(", ")}!`;
}
Destructuring parameters
function printUser({ name, age, city = "Seoul" }) {
console.log(`${name} (${age}, ${city})`);
}
function getMinMax([first, ...rest]) {
return {
min: Math.min(first, ...rest),
max: Math.max(first, ...rest)
};
}
Returning multiple values
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();
function getUserInfo() {
return { name: "Alice", age: 25, city: "Seoul" };
}
const { name, age } = getUserInfo();
3. Higher-order functions
Functions as arguments
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, console.log);
repeat(3, i => console.log(`iteration ${i}`));
Functions that return functions
function makeMultiplier(factor) {
return function(x) {
return x * factor;
};
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
const makeMultiplier = factor => x => x * factor;
Array higher-order methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
const evens = numbers.filter(x => x % 2 === 0);
const sum = numbers.reduce((acc, x) => acc + x, 0);
const result = numbers
.filter(x => x % 2 === 0)
.map(x => x * 2)
.reduce((acc, x) => acc + x, 0);
4. Closures
What is a closure?
A closure is a function that remembers variables from the scope where it was created.
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
const counter2 = makeCounter();
console.log(counter2()); // 1
Private state
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
if (amount > 0) balance += amount;
return balance;
},
withdraw(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return balance;
}
return null;
},
getBalance() {
return balance;
}
};
}
More examples
function makeAdder(x) {
return y => x + y;
}
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (key in cache) return cache[key];
const result = fn(...args);
cache[key] = result;
return result;
};
}
5. this binding
Ordinary functions
const person = {
name: "Alice",
greet: function() {
console.log(`Hello, ${this.name}.`);
}
};
person.greet();
const greetFunc = person.greet;
// greetFunc(); // loses `this` in non-strict sloppy mode / undefined in strict
Arrow functions and this
const person = {
name: "Alice",
hobbies: ["reading", "sports", "coding"],
printHobbies2: function() {
this.hobbies.forEach(hobby => {
console.log(`${this.name}'s hobby: ${hobby}`);
});
}
};
person.printHobbies2();
bind, call, apply
const person = { name: "Alice" };
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
greet.call(person, "Hello", "!");
greet.apply(person, ["Hello", "."]);
const boundGreet = greet.bind(person);
boundGreet("Hi", "~");
6. Callbacks
Basics
function processArray(arr, callback) {
const result = [];
for (let item of arr) {
result.push(callback(item));
}
return result;
}
Async callbacks
console.log("start");
setTimeout(() => {
console.log("after 2s");
}, 2000);
console.log("end");
setTimeout(() => {
console.log("1s");
setTimeout(() => {
console.log("2s");
setTimeout(() => console.log("3s"), 1000);
}, 1000);
}, 1000);
7. IIFE
(function() {
console.log("IIFE");
})();
(() => {
console.log("arrow IIFE");
})();
const result = (function() {
return 10 + 20;
})();
Module-style IIFE
const counterModule = (function() {
let count = 0;
return {
increment() { count++; return count; },
decrement() { count--; return count; },
getCount() { return count; }
};
})();
8. Recursion
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
function sumArray(arr) {
if (arr.length === 0) return 0;
return arr[0] + sumArray(arr.slice(1));
}
9. Practical examples
Utilities: chunk, flatten, unique
function chunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
function flatten(arr) {
return arr.reduce((acc, item) =>
acc.concat(Array.isArray(item) ? flatten(item) : item), []);
}
function unique(arr) {
return [...new Set(arr)];
}
Composition
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
Debounce and throttle
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
function throttle(func, delay) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
func(...args);
}
};
}
10. Common mistakes
Arrow functions as methods
const person = {
name: "Alice",
greet: () => {
console.log(this.name); // wrong: lexical this
}
};
Closures in loops with var
for (var i = 0; i < 3; i++) {
setTimeout(function() { console.log(i); }, 100);
}
for (let i = 0; i < 3; i++) {
setTimeout(function() { console.log(i); }, 100);
}
11. Exercises
Currying
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) return fn(...args);
return (...nextArgs) => curried(...args, ...nextArgs);
};
}
Pipeline
function pipe(...fns) {
return function(x) {
return fns.reduce((acc, fn) => fn(acc), x);
};
}
Summary
Key points
- Definitions: declarations hoist; expressions and arrows do not behave like declarations.
- Parameters: defaults, rest, destructuring.
- Higher-order: pass/return functions;
map/filter/reduce. - Closures: encapsulation and memoization.
this: dynamic in ordinary methods; lexical in arrows—choose accordingly.
Best practices
- Prefer arrows for short callbacks when
thisshould come from outside. - Write small, pure functions when possible.
- Name functions clearly; limit parameter count.
Next steps
- JavaScript arrays and objects
- JavaScript async programming
- JavaScript classes
Related posts
- JavaScript async programming | Promises and async/await