
함수는 자바스크립트에서 오지게 중요한 블록이다. 같은 코드 계속 복붙할 바에, 그냥 한 군데 쳐넣고 이름 하나 박아두면 된다. 입력 들어오면 겁나 굴려서 처리하고 결과를 던지신다. 함수는 선언(또는 표현)된 스코프(scope) 안에서만 불러와 부려먹을 수 있고, 입력과 출력 사이에는 이해 가능한 관계가 존재해야 한다.
함수 선언문은 function 키워드, 함수 이름, 괄호 안 매개변수 목록, 그리고 중괄호 블록으로 구성된다.
function square(number) {
return number * number;
}
function rename(car) {
car.make = "Toyota"; // 프로퍼티 변경 → 바깥에서도 보임
}
const myCar = { make: "Honda" };
rename(myCar);
console.log(myCar.make); // "Toyota"
함수는 표현식으로도 만들 수 있다. 익명일 수도 있고, 이름을 가진 표현식으로도 작성할 수 있다.
// 익명 함수 표현식
const square = function (n) {
return n * n;
};
// 이름이 있는 함수 표현식 (재귀/디버깅에 유리)
const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
};
표현식은 값처럼 전달될 수 있어 고차함수 인자에 쓰기 좋다.
function map(f, arr) {
const out = new Array(arr.length);
for (let i = 0; i < arr.length; i++) out[i] = f(arr[i]);
return out;
}
const cubed = map(function (x) { return x * x * x; }, [0, 1, 2, 5, 10]);
console.log(cubed); // [0, 1, 8, 125, 1000]
조건에 따라 동적으로 정의할 수도 있다(필요한 경로에서만 생성).
let myFunc;
if (num === 0) {
myFunc = function (obj) { obj.make = "Toyota"; };
}
참고: 런타임에 문자열로부터 함수를 만드는 Function 생성자도 있지만, 보안·최적화 측면에서 권장되지 않는다.
메서드는 객체의 프로퍼티로 존재하는 함수이다.
const counter = {
value: 0,
inc() { this.value += 1; },
};
counter.inc();
함수를 정의하는 것과 호출하는 것은 별개이다. 호출 시 인자를 넘기고, 함수는 지정된 로직을 실행해 값을 반환한다.
function square(n) { return n * n; }
square(5); // 25
함수는 자기 자신을 호출(재귀)할 수 있다.
function factorial(n) {
if (n === 0 || n === 1) return 1;
return n * factorial(n - 1);
}
함수도 객체이므로, 호출 문맥을 제어하는 메서드를 가진다.
fn.call(thisArg, ...args)fn.apply(thisArg, argsArray)fn.bind(thisArg) → 새 함수 반환(지연 호출용)선언문은 스코프의 최상단으로 끌어올려진 것처럼 동작한다.
console.log(square(5)); // 25
function square(n) { return n * n; }
반면 함수 표현식은 호이스팅되지 않는다(변수 초기화 전 접근 불가).
console.log(square(5)); // ReferenceError
const square = function (n) { return n * n; };
재귀는 루프와 유사하게 반복 기제로 쓰이며, 탈출 조건이 필수다. 호출은 스택에 쌓였다가 역순으로 정리된다.
function foo(i) {
if (i < 0) return;
console.log(`begin: ${i}`);
foo(i - 1);
console.log(`end: ${i}`);
}
foo(3);
// begin: 3 → 2 → 1 → 0 → end: 0 → 1 → 2 → 3
트리 순회처럼 자연스러운 계층 구조에는 재귀가 간결하다.
function walk(node) {
if (!node) return;
// do something with node
for (const child of node.childNodes) walk(child);
}
정의하자마자 즉시 호출하는 패턴으로, 독립된 스코프를 마련하거나 초기화 로직을 캡슐화할 때 유용하다.
const getCode = (function () {
const secret = "0]Eal(eh&2";
return function () { return secret; };
})();
console.log(getCode()); // 비공개 데이터 접근
함수는 자기만의 스코프를 만든다. 내부 함수는 바깥 스코프의 식별자를 기억하고, 바깥 스코프 수명이 끝난 뒤에도 접근할 수 있다 → 클로저.
const pet = function (name) {
return function getName() { return name; }; // name을 캡처
};
const myPet = pet("Vivie");
console.log(myPet()); // "Vivie"
여러 함수를 묶어 상태를 은닉할 수도 있다.
const createPet = function (name) {
let sex;
return {
setName(newName) { name = newName; },
getName() { return name; },
setSex(newSex) {
const s = String(newSex).toLowerCase();
if (s === "male" || s === "female") sex = s;
},
getSex() { return sex; },
};
};
스코프 체이닝: 더 안쪽 스코프가 언제나 우선한다(이름 충돌 시 내부가 승리).
function outside() {
const x = 5;
function inside(x) { return x * 2; }
return inside;
}
console.log(outside()(10)); // 20 (내부 x가 우선)
arguments 객체모든 함수 내부에는 배열 비슷한 arguments가 있다(ES5 스타일). 인덱스와 length는 있으나, 배열 메서드는 없다.
function myConcat(separator) {
let out = "";
for (let i = 1; i < arguments.length; i++) {
out += arguments[i] + separator;
}
return out;
}
console.log(myConcat(", ", "red", "blue")); // "red, blue, "
실무 팁: ES6 이후에는 rest 파라미터(...args)가 더 안전하고 타입 친화적이다.
기본값 파라미터
function multiply(a, b = 1) {
return a * b;
}
multiply(5); // 5
과거에는 본문에서 typeof b === "undefined"로 체크했으나, 이제는 머리에서 바로 지정한다.
Rest 파라미터
function multiply(multiplier, ...nums) {
return nums.map((x) => multiplier * x);
}
multiply(2, 1, 2, 3); // [2, 4, 6]
짧은 문법과 this 비바인딩(렉시컬 this)이 특징이다. 자체 this/arguments/super/new.target을 가지지 않는다. 항상 익명이다.
const a = ["Hydrogen", "Helium", "Lithium", "Beryllium"];
const len1 = a.map(function (s) { return s.length; });
const len2 = a.map((s) => s.length); // 간결
렉시컬 this: 바깥 컨텍스트의 this를 그대로 사용한다. 콜백에서 self = this 해법이나 bind 없이 의도가 보존된다.
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // Person 인스턴스를 가리킴
}, 1000);
}
const p = new Person();
call/apply/bind로 문맥·인자 제어.this로 콜백 코드 안정화.요약하면, 함수는 로직 재사용의 단위이자 스코프의 경계이다. 선언 방식(선언문/표현식), 호출 문맥(this), 파라미터 문법(기본값/Rest), 그리고 클로저를 이해하면 JavaScript 코드의 구조와 유지보수성이 눈에 띄게 좋아진다.