변수 선언이 var로 시작해야하는 것처럼 함수 선언은 function으로 시작한다.
선언 된 함수는 나중 사용을 위해 저장되며, call 될 때 실행된다.
function foo() {
return "이것은 함수입니다.";
}
함수는 여기서만 선언되며, 사용하려면 해당 함수 이름을 호출하면 된다.
foo(); // "이것은 함수입니다."
자바스크립트 함수는 표현식을 사용하여 정의 될 수 있으며, 함수 표현식은 변수로 저장될수 있다.
var x = function (a, b) {return a * b};
함수 표현식이 변수에 저장되면, 변수는 함수처럼 사용 가능해진다. 변수에 저장된 함수는 함수명이 필요 없으며, 변수 이름을 통하여 호출된다.
함수 표현식
alert(foo()); // 에러 발생! foo 함수는 아직 로드안됨
var foo = function() { return 5; }
함수 선언식
alert(foo());
// Alerts 5.
// 선언 전에 호출되도 정상 동작
function foo() { return 5; }
함수 선언식은 코드가 실행되기 전에 로드되지만, 함수 표현식은 인터프리터가 해당 코드 줄에 도달 할 때만 로드된다.
함수 선언식은 var 문과 유사하게 호이스팅(호이스팅 이란?) 된다. 반면, 함수 표현식은 호이스팅되지 않으므로 정의 된 범위에서 로컬 변수의 복사본을 유지할 수 있다.
일반적으로 함수 선언식과 함수 표현식은 함께 사용할 수는 있지만, 함수 표현식은 함수 이름이 필요없기에 가독성이 더 높은 장점이 있다.
함수 선언식과 비교하여, 함수 표현식의 이점은 아래의 경우로 나뉜다.
- 클로저
- 인자 전달
- IIFE
클로저(클로저 란?)는 함수가 종료돼도, 렉시컬 스코프의 index와 같은 정보를 유지한다.
function navsHandler(index) {
return function navClickEvent(evt) {
// 이벤트 코드
}
}
var navs = document.querySelectorAll('.nav')
var i
for (i = 0; i < navs.length; i += 1) {
navs[i].onclick = navsHandler(i)
}
이벤트 핸들러는 반복이 완료된 후 실행되므로, for 루프의 올바른 값을 유지하려면 클로저가 필요하다.
// 잘못된 예 1
var i
for (i = 0; i < list.length; i += 1) {
document.querySelector('#item' + i).onclick = function doSomething(evt) {
// i는 항상 list.length 값을 갖는다.
}
}
// 잘못된 예 2
var list = document.querySelectorAll('.item')
var i
var doSomething = function (evt) {
// 이 함수가 실행될 때까지 i의 값은 루프에 있던 값이 아니다.
};
for (i = 0; i < list.length; i += 1) {
item[i].onclick = doSomething;
}
여기서 해결책은 index를 인자로 외부 함수에 전달하여 해당 값을 내부 함수에 전달할 수 있도록하는 것이다. 일반적으로 내부 반환 함수에 필요한 정보를 구성하는 데 사용되는 핸들러 함수는 아래와 같다.
// 좋은 예
var list = ['item1', 'item2', 'item3']
var i
var doSomethingHandler = function (itemIndex) {
return function doSomething(evt) {
// 클로저가 생성되어, itemIndex를 인자로 참조 할 수 있게 된다.
console.log(list[itemIndex]);
};
};
for (i = 0; i < list.length; i += 1) {
list[i].onclick = doSomethingHandler(i);
}
함수 표현식은 중간 임시 변수에 할당 할 필요없이 함수에 직접 전달할 수 있다.
익명 함수의 형태로 jQuery에서 자주 볼 수 있다. 예를 들면 다음과 같다.
$(document).ready(function () {
console.log('익명 함수');
});
forEach()와 같은 메서드는 함수 표현식이 배열 항목을 처리하는 데 사용된다. 해당 함수가 익명 함수일 필요는 없다. 함수가 수행해야하는 작업을 표현하고 디버깅을 돕기 위해 함수 표현식의 이름을 지정하는 것이 좋다.
var productIds = ['bisu', 'boxer', 'nada'];
productIds.forEach(function showProduct(productId) {
...
});
IIFE는 함수와 변수가 전역 스코프에 영향을 미치지 않도록 방지하는 데 사용된다. IIFE 내의 모든 속성은 익명 함수로 범위가 지정된다. 이는 코드가 다른 곳에서 부작용이 발생하지 않도록 방지하는 데 사용되는 일반적인 디자인 패턴이다.
또한 유지 관리하기 쉬운 섹션에 코드 블록을 포함하는 모듈 패턴으로도 사용된다. 그 예는 아래와 같다.
(function () {
// 코드 ...
}());
모듈로 사용하면 코드를 쉽게 유지 관리 할 수 있다.
var myModule = (function () {
var privateMethod = function () {
console.log('A private method');
},
someMethod = function () {
console.log('A public method');
},
anotherMethod = function () {
console.log('Another public method');
};
return {
someMethod: someMethod,
anotherMethod: anotherMethod
};
}());
함수 표현식은 함수 선언식 보다 깔끔하고 가독성이 높은 코딩을 하게한다. 함수 표현식/선언식의 장단점 파악은 자바스크립트 디자인 패턴 공부에 있어서 필수로 알아야 하는 기초 단계일 것이다.