위 블로그는 생활코딩 사이트에서 클로저에 대한 강의를 듣고 스스로 이해한 뒤, 해당 사이트를 상당 부분 참고하여 작성한 내용이다.
클로저(closure)는 내부함수가 외부함수의 맥락에 접근할 수 있는 것을 가리킨다.
function outter() {
let title = 'Hello World!';
function inner() {
console.log(title);
}
inner();
}
outter(); // 'Hello World!'
이 예제는 내부함수 inner에서 title을 호출했을 때, 외부함수 outter의 지역변수인 title에 접근할 수 있음을 보여준다.
function outter() {
let title = 'Hello World!';
return function() {
console.log(title);
}
}
inner = outter();
inner(); // 'Hello World!'
위 예제의 마지막 두 행을 보자.
함수 outter를 호출함으로써 변수 inner에 function() { console.log(title) }
이 담겼다.
그런 후, 함수 inner를 다시 호출함으로써 'Hello World!'가 출력됨을 확인할 수 있다.
원래대로 라면 inner = outter()
라는 코드에서 outter 함수는 실행이 끝났기 때문에 해당 함수의 지역변수는 소멸해야 한다. 하지만, inner()
를 실행했을 때 'Hello World!'가 출력되는 것으로 보아 함수 outter의 지역변수 title이 소멸하지 않았다는 것을 알 수 있다.
이를 통해 우리는 클로저가 하는 역할에 대해 다시 생각해볼 수 있다.
클로저는 내부함수가 외부함수의 지역변수에 접근할 수 있고, 외부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸할 때까지 소멸하지 않는 특성을 의미한다.
다음으로는, 클로저를 통해 사용할 수 있는 private한 속성에 대해 알아보겠다.
function myFavoriteFood(name) {
return {
get_name : function() {
return name;
},
set_name : function(_name) {
name = _name;
}
}
}
pizza = myFavoriteFood('pizza');
soup = myFavoriteFood('soup');
console.log(pizza.get_name()); // 'pizza'
console.log(soup.get_name()); // 'soup'
pizza.set_name('피자');
console.log(pizza.get_name()); // '피자'
console.log(soup.get_name()); // 'soup'
위 예제를 통해 알 수 있는 것들은 다음과 같다.
클로저는 객체의 메소드에서도 사용할 수 있다.
위 예제를 보면 함수 myFavoriteFood의 리턴값으로 객체를 반환하고 있다. 이 객체는 메소드 get_name과 set_name을 가지고 있다. 해당 메소드들은 외부함수인 myFavoriteFood의 인자값으로 전달된 지역변수 name을 사용하고 있다. 이를 통해 객체의 메소드 또한 외부함수의 지역변수에 접근할 수 있다는 것을 알 수 있다.
동일한 외부함수 안에서 만들어진 내부함수나 메소드는 외부함수의 지역변수를 공유한다.
pizza.set_name('피자')
는 지역변수 name의 값을 '피자'로 변경했다. console.log(pizza.get_name())
의 값이 '피자'인 것은 get_name과 set_name 메소드가 외부함수의 지역변수인 name의 값을 공유하고 있다는 의미이다.
똑같은 외부함수 myFavoriteFood를 공유하고 있는 pizza와 soup의 get_name의 결과는 각각 다르다.
그 이유는 외부함수가 실행될 때마다 새로운 지역변수를 포함하는 클로저가 생성되기 때문에 pizza와 soup은 각각 완전히 독립된 객체가 된다.
myFavoriteFood의 지역변수 name은 두 번째 행에서 정의된 객체의 메소드에서만 접근할 수 있는 값이다. 이 말은 name의 값을 읽고 수정할 수 있는 것은 myFavoriteFood 메소드를 통해서 만들어진 객체 뿐이라는 의미다.
JavaScript는 기본적으로 private한 속성을 지원하지 않는데, 클로저의 이러한 특성을 이용하여 private한 속성을 사용할 수 있다.
참고: Private 속성은 객체의 외부에서는 접근할 수 없는 외부에 감춰진 속성이나 메소드를 의미한다. 이를 통해서 객체의 내부에서만 사용해야 하는 값이 노출됨으로써 생길 수 있는 오류를 줄일 수 있다.