let number = 0;
function sum (n){
return number + n;
}
console.log(
number가 있고 number에 n을 더해주는 함수가 있다. number는 전역에 나와있어서 console.log를 통해 직접 값을 확인할 수 있다.
하지만 number를 비밀유지를 위해서 숨겨야 할 때가 있을것이다.
또한 계산을 간결하게 묶어주고 싶은 순간이 있다. 이 것을 해결해주는 함수가 클로저 함수다.
클로저는 함수와 함수가 선언된 어휘적(lexical) 환경의 조합...이라고 MDN은 설명하고 있다.
하지만 어휘적 환경? 너무 정의가 어렵다. 그래서 이렇게 더 간단하게 설명할 수 있다.
내부함수가 외부에 선언된 변수에 접근할 수 있는 것
즉 외부함수에 접근할 수 있는 내부함수
그렇다면 왜 lexical 환경이라고 쓰게 되었을까?
함수와 함수가 선언된 어휘적 환경의 조합
여기서 함수는 외부함수가 아닌 내부함수를 의미한다. 그리고 lexical 환경이라는 것은 내부함수가 선언되었을 때의 스코프를 의미한다.
즉 내부함수는 자신의 스코프(전역변수, 외부함수 등등)에 있는 변수를 참조할 수 있다.
가장 간단한 예시이다.
function addText(){
let firstText = "hello"
let nextText = function(next){
return firstText + next;
}
return nextText
}
let hello = addText();
console.log(hello(" 영희")) // "hello 철수"
console.log(hello(" 철수")) // "hello 영희"
console.log(fisrtText); //Uncaught ReferenceError: fisrtText is not defined
예시를 보듯이 가장 대표적인 클로저 사용의 이유는 3가지이다.
캡슐화 와 은닉화, 그리고 변수 저장
복잡한 계산식이 있다고 가정한다. 그리고 그 계산은 매우 자주 쓴다고 생각한다면, 계산식을 사용하기 앞서서 하나로 묶어준다면 이용하기 쉽게 사용할 수 있다.
위의 예시에서도 hello를 정의하고 addText를 넣어줌으로써 내부함수의 복잡한 과정을 간략하게 정리해주었다.
이렇게 기능을 하나로 묶어주는 캡슐화를 통해서 코드를 간결하게 짤 수 있게 도와준다.
세번째 console.log를 주목해보자. firstText는 외부에서는 읽을 수가 없다. 함수 내에서 정의된 변수이기 때문이다. 따라서 저 변수는 함수를 뜯어보지 않는 이상은 확인할 수 없다.
function makeSalted(){
let salt = "Iloveyou";
let merged = function(text){
return salt + text
}
return merged
}
만약 위의 예시가 아닌 salt를 더하여 해쉬값을 만드는 함수라면 salt라는 값은 절대 전역변수에 두면 안된다. 자칫하면 비밀정보가 드러날 수 있기 때문이다. 그래서 클로저 내에 두게 된다면 salt값을 효율적으로 숨길 수 있게 된다.
이처럼 클로저는 전역변수의 남용으로 인한 보안위험을 줄이고 효과적으로 변수를 은닉할 수 있는 기능을 제공한다.
function count(){
let data = 0;
let clicked = function(){
data++;
return data;
}
return clicked
}
let clickCount= count();
console.log(clickCount());// 1
console.log(clickCount());// 2
console.log(clickCount());// 3
내부함수 clicked는 data의 값을 기억하고 있다. 따라서 초기화가 되는 것이 아니라 계속해서 더해주게 된다.
이것을 응용하면 toggle 기능도 만들 수 있게 된다.
function toggleButton(){
let data = false;
let clicked = function(){
data?data = false:data = true;
return data;
}
return clicked
}
let onClick = toggleButton();
console.log(onClick()); //true
console.log(onClick()); //false
//이 것을 통해 버튼 클릭 상태를 제어할 수 있다.