JavaScript: Closures
1. Lexical Scope 와 클로저(Closures)
보통 함수를 정의할 때, scope란 유효범위를 나타내는 용어이다. (물론, OS 까지 들어가면, 정확한 정의는 아니지만…)
Javascript는 함수 정의, 호출 시 Lexical scope를 따르는데, Lexical scope는 함수가 어디서 정의되었는지에 따라 상위 스코프를 결정하는 방식이다.
function init() {
let word = 'Fire';
function showWord() {
console.log(word); // Fire
}
showWord();
}
init();
자식함수(showWord)에서 부모함수의 변수(word)를 참조할 수 있었던 이유는 자식함수가 정의되었을 때의 상위 스코프를 기억하여 부모함수의 변수를 참조할 수 있었던 까닭이다.
여기에서 클로저는 자식함수와 부모함수의 구조에서 부모함수의 변수를 참조할 수 있는 자식함수를 말한다.
내용이 많아 복잡할 수 있지만 이렇게 단순히 기억해도 될 것 같다.
- 어떠한 함수의 변수를 다른 함수에서도 공유하고 싶다면, 공유받고 싶은 함수를 클로저로 만들어 공유받을 수 있게 할 수 있다.
아래는 직접 작성해본 클로저의 응용 예시이다.
- 함수A에 배열을 인수로 호출하여, 페이지에 느낌표가 결합된 단어로 출력하고 싶다.
- 예) 함수A[a, b, c] => a! b! c! 가 HTML 요소에 각각 출력
- 함수는 2개로 구성한다. 예를 들어, 함수A는 인수를 매개변수에 전달하고 느낌표가 결합된 단어로 만들어야 하고, 함수B는 이렇게 결합된 단어를 HTML 요소에 출력해야한다.
- 호출은 한번에 위에서 정의한 2개의 함수가 작동하도록 한다.
- 이것이 가능하려면, 함수A와 B에서 모두 느낌표가 결합된 단어들의 배열(changeAlphabet)을 참조할 수 있어야 한다.
const row = document.querySelectorAll('.row');
const showAlphabet = (alphabets = []) => {
// changeAlphabet을 공유하고 싶음
const changeAlphabet = alphabets.map((el)=> {
return el + '!'
})
let i = 0;
// 함수 show는 클로저
const show = () => {
row.forEach((el) => {
el.innerText = changeAlphabet[i]; // a!, b!, d! 표시
i++;
})
}
show();
}
showAlphabet(['a', 'b', 'd']);
2. 클로저 체이닝
클로저는 아래와 같이 세가지 스코프를 갖게 되고, 모든 스코프에 접근할 수 있다.
- 지역 범위
- 외부 함수 범위
- 전역 범뮈
// ====익명함수를 사용하여 클로저 체이닝한 예====
// 전역 범위
const sum1 = (a) => {
return (b) => {
return (c) => {
// 외부 함수 범위
return (d) => {
// 지역 범위
return a + b + c + d;
}
}
}
}
console.log(sum1(1)(2)(3)(4)) // 10
// 익명함수 없이 클로저 체이닝한 예
const mul1 = (a) => {
return mul2 = (b) => {
return mul3 = (c) => {
return mul4 = (d) => {
return a * b * c * d;
}
}
}
}
// 에러
// console.log(mul1(a)(b)(c)(d));
const M1 = mul1(1);
const M2 = M1(2);
const M3 = M2(3);
const M4 = M3(4);
console.log(M4); // 24
정리
- JS에서 중첩된 함수의 내부함수(자식함수)를 ‘클로저’라고 한다.
- 클로저는 세개의 스코프(전역, 지역, 외부함수)를 갖게 되고, 모두 접근할 수 있다.
댓글남기기