📖 정의
- 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킴
- 유효범위(scope)에 묶인 변수를 바인딩 하기 위한 일종의 기술
- 함수를 저장한 레코드(record)이며, 스코프(scope)의 인수(Factor)들은 클로저가 만들어질 때 정의
- 독립적인 변수(로컬로 사용되지만 둘러싼 범위에서 정의 된 변수)를 참조하는 함수
- 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미
- 함수와 그 함수가 선언된 렉시컬 환경과의 조합
⏳ 이해하고 넘어가야 하는 것
렉시컬 스코프
자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다.
내부 함수
자바스크립트는 함수 안에서 또 다른 함수를 선언할 수 있다.
예제
function outter(){ //외부 함수
function inner(){ //내부 함수
var title = 'coding everybody';
alert(title);
}
inner();
}
outter();
// output : coding everybody
위의 예제에서 함수 outter의 내부에는 함수 inner가 정의 되어 있다. inner함수를 내부 함수라고 한다.
내부함수는 외부함수의 지역변수에 접근할 수 있다.
function outter(){
var title = 'coding everybody'; // 외부 함수에 선언된 지역 변수
function inner(){
alert(title); // title을 위 지역 변수의 값을 갖게 됨
}
inner();
}
outter();
// output : coding everybody
위의 내부함수 inner에서 title을 호출(4행)했을 때 외부함수인 outter의 지역변수에 접근할 수 있음을 보여준다. 👉🏻 여기서 inner함수가 바로 클로저
📚 클로저
내부함수는 외부함수의 지역변수에 접근 할 수 있는데 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근 할 수 있다. 이러한 메커니즘을 클로저라고 한다.
예제
1 function outter(){ // 외부 함수 정의
2 var title = 'coding everybody';
3 return function(){ // 내부 함수 정의를 하고 return 하고 있음
4 alert(title);
5 }
6 }
7 var inner = outter(); // inner에 내부 함수를 담음
8 inner(); // output : 'coding everybody'
외부함수는 return으로 종료됐지만 외부함수에 접근을 했을 때 접근이 성공적으로 이루어짐.
🕑실행순서
1️⃣ 7행에서 outter함수를 호출함 👉🏻 결과가 변수 inner에 담김 👉🏻 결과는 이름이 없는 함수
2️⃣ 8행에서 outter함수는 실행이 끝났기 때문에 이 함수의 지역변수는 소멸되는 것이 자연스러움. 하지만
coding everybody가 출력된 것은 외부함수의 지역변수 title이 소멸되지 않았다는 것을 의미한다.
💡클로저란 내부함수가 외부함수의 지역변수에 접근 할 수 있고, 외부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸될 때까지 소멸되지 않는 특성을 의미한다.
조금 더 복잡한 예제
1 function factory_movie(title){
2 return {
3 get_title : function (){ // 메서드
4 return title; // factory_movie의 매개변수인 title 반환
5 },
6 set_title : function(_title){
7 title = _title // title은 _title로 변함
8 }
9 }
10 }
11 ghost = factory_movie('Ghost in the shell'); //get_title와 set_title를 담고있는 객체 반환
12 matrix = factory_movie('Matrix');
14 alert(ghost.get_title());
15 alert(matrix.get_title());
17 ghost.set_title('공각기동대');
19 alert(ghost.get_title());
20 alert(matrix.get_title());
//output : Ghost in the shell -> Matrix -> 공각기동대 -> Matrix
위의 예제를 통해서 알 수 있는 것들을 정리해보면 아래와 같다.
1. 클로저는 객체의 메소드에서도 사용할 수 있다. 위의 예제는 함수의 리턴값으로 객체를 반환하고 있다. 이 객체는 메소드 get_title과 set_title을 가지고 있다. 이 메소드들은 외부함수인 factory_movie의 인자값으로 전달된 지역변수 title을 사용하고 있다.
2. 동일한 외부함수 안에서 만들어진 내부함수나 메소드는 외부함수의 지역변수를 공유한다. 17행에서 실행된 set_title은 외부함수 factory_movie의 지역변수 title의 값을 '공각기동대'로 변경했다. 19행에서 ghost.get_title();의 값이 '공각기동대'인 것은 set_title와 get_title 함수가 title의 값을 공유하고 있다는 의미다.
3. 그런데 똑같은 외부함수 factory_movie를 공유하고 있는 ghost와 matrix의 get_title의 결과는 서로 각각 다르다. 그것은 외부함수가 실행될 때마다 새로운 지역변수를 포함하는 클로저가 생성되기 때문에 ghost와 matrix는 서로 완전히 독립된 객체가 된다.
⭐⭐⭐
4. factory_movie의 지역변수 title은 2행에서 정의된 객체의 메소드에서만 접근 할 수 있는 값이다. 이 말은 title의 값을 읽고 수정 할 수 있는 것은 factory_movie 메소드를 통해서 만들어진 객체 뿐이라는 의미다. JavaScript는 기본적으로 Private한 속성을 지원하지 않는데, 클로저의 이러한 특성을 이용해서 Private한 속성을 사용할 수 있게된다.
클로저 장점
❗ 은닉
클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.
상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용한다.
❗ 데이터 보존
외부 함수의 실행이 끝나도 외부 함수 내 변수를 사용할 수 있다.
특정 데이터를 스코프 안에 넣어 둔 채로 계속 사용할 수 있는 폐쇄성을 가진다.
❗ 캡슐화
'클로저 모듈 패턴'을 사용해 객체에 담아 여러 개의 함수를 리턴하도록 한다.
이처럼 정보의 접근을 제한하는 것을 캡슐화라고 한다.
❗모듈화
클로저 함수의 각각의 변수를 할당하면 각자 독립적으로 값을 사용하고 보존할 수 있다.
재사용성을 극대화 하고 함수 하나를 독립적인 부품의 형태로 분리한다.
클로저 특징
❗스코프를 활용해, 변수의 접근 범위를 폐쇄한다.
외부함수 스코프에서 내부함수 스코프로 접근 불가능하다.
내부함수에서 외부함수 스코프에서 선언된 변수에 접근 가능하다.
❗ 함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 환경을 기준으로 변수를 조회한다.
외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉 함수가 선언된 어휘적 환경에 접근 할 수 있다.
외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있다.
❗ 상위 스코프의 어떤 식별자도 참조하지 않는 함수는 클로저가 아니다
자바스크립트의 모든 함수는 상위 스코프를 기억하므로 이론적으로는 모든 함수가 클로저이다.
하지만 모든 함수를 클로저라고 하지 않는다.
상위 스코프의 어떤 식별자도 참조하지 않는 것은 메모리 낭비이기 때문에 클로저라고 부르지 않는다.
클로저 응용
예제
문제. 0부터 1까지 출력한다고 해보자.
var arr = []
for(var i = 0; i < 5; i++){
arr[i] = function(){
return i;
}
}
for(var index in arr) {
console.log(arr[index]());
}
// output :
// 5
// 5
// 5
// 5
// 5
🧐 왜 5가 5번 나올까?
✔ i의 값이 정의한 함수의 외부 변수의 값이 아니기 때문이다.
🧐 그렇다면?
✔ 함수를 내부함수로 하는 외부함수를 정의하고, 외부함수의 지역변수의 값을 내부함수가 참조하도록 해야한다!
고친 예제
var arr = []
for(var i = 0; i < 5; i++){
arr[i] = function(id) { // 외부함수 생성, id라는 매개변수(지역변수와 같은 효과)를 가짐
return function(){
return id;
}
}(i); // 매개변수로 i를 줌
}
for(var index in arr) {
console.log(arr[index]());
}
// output :
// 0
// 1
// 2
// 3
// 4
'JavaScript' 카테고리의 다른 글
[JS] 예외 처리(exception handling) (0) | 2022.07.06 |
---|---|
[JS] 2진수, 10진수, 8진수, 16진수 변환하기 (0) | 2022.06.15 |
[JS] 자료 다루기(객체, 배열) (1) | 2022.06.05 |
[JS] 정규표현식(Regex) (3) | 2022.05.10 |