참고영상: 인프런 코어자바스크립트
⭐️ closure
- "닫힘/폐쇄/완결성" 이라는 사전적 의미
- 하지만 자바스크립트에서는 "결합, 조화, 조합"의 의미
- 둘러싸인 lexical environment의 참조
- 한마디로 내부함수와 LexicalEnvironment의 조합
- 어떤 실행컨텍스트a에서 함수 b를 선언 -> A의 lexical environment와 내부함수 B의 조합의 "특별한 현상"
📚 실행 컨텍스트 그림으로 알아보기
📌 A의 outerEnvironmentReference는 어차피 B도 똑같이 접근이 되니까 B랑은 직접 관련이되지 않음
📌 B의 environmentRecord는 B 내부에서 선언한 식별자만 관련이 있으니까 A랑은 관련이 없음
📌 하지만, B의 outerEnvironmentReference로 A의 environmentRecord에 접근 가능하니, 이 둘 사이의 조합을 다루는 것
✅ 즉, 컨텍스트 A에서 선언한 변수를 내부함수 B에서 참조할 경우에 발생하는 특별한 현상
💻 코드 1-1)
var outer = function () {
var a=1;
var inner = function() {
console.log(++a);
};
inner();
}
outer();
📌 outer함수 안에 a 변수가 선언되어 있다. outer 컨텍스트에서 선언한 변수(a)를 내부함수 innter에서 참조
📚 코드 1-1 실행 컨텍스트 그림으로 알아보기
📌 처음, 전역 컨텍스트가 열리고 아우터 함수를 실행하여 아우터 실행 컨텍스트가 쌓임
📌 outer Context에는 Lexical Environment 안에 environmentRecord가 존재하는데 이 레코드 안에 a는 1이 들어가 있고 inner라는 함수가 들어가있음. 또, outerEnvironmentReference에는 outer 함수가 들어가있음
📌 inner 함수를 실행했을 때, 또 그 위에 inner context가 쌓임. inner Context 안에 Environment Record에는 아무것도 없음(선언한게 없으니까). 그리고 outerEnvironmentReference에는 outer context environment를 참조
📌 이 배경으로 inner 함수를 실행시켰을 때, inner context가 사라지고 그 다음 아우터 함수를 실행시키면 outer context가 사라지고 끝으로 전역 컨텍스트가 사라지면서 끝남
💻 코드 1-2 )
var outer = function() {
var a=1;
var inner = function () {
return a++;
};
return inner;
}
var outer2 = outer();
console.log(outer2()); //2
console.log(outer2()); //3
📚 코드 1-2 실행 컨텍스트 그림으로 알아보기
📌 처음 전역 컨텍스트에 outer 함수가 정의되어 있고, outer 함수가 정의되었지만, outer 함수가 아직 호출되기 전에는 outer2 함수는undefined로 존재 ( outer 함수가 호출되어서 outer함수의 정의를 내린 후 outer2 함수의 정의내릴 수 있으니까)
📌 그리고 outer 함수 안에 inner 함수가 존재 (위치는 environmentRecord)
📌 그 후, outer 함수가 호출이 되었다면, outer2에서는 inner 함수가 담기게 됨
📌 outer 함수가 실행이 되었으니까, 그림처럼 밑줄 표시를 해놓았지만 a 라는 변수는 아직 살아있는 상태라 밑줄을 치지 않음
📌 즉, outer 함수는 종료되어 실행 컨텍스트 또한 종료되었지만 이 함수 내부에 있는 변수 a는 죽지 않음. 왜냐하면, 참조 카운트가 0이 되지 않았으니까, 언젠간 저 변수를 쓸 수 있으니까.
📌 그래서, 다시 두번째로 console.log(outer2)를 불러왔을 때, inner 함수 내에서 outer 컨텍스트에 있는 변수 a를 살려놨기 때문에, 두 번째로 outer2 함수를 호출했을 때 2라는 결과값을 출력할 수 있게 됨
📌 만약 이 closure 함수를 끊어내고 싶다면, outer2 함수에서 inner 함수 대신 다른 함수를 대입하면 될 것임. 그러면 연결고리가 끊겨 연쇄적으로 GC 대상이 될 것임
📚 정리
✅ 컨텍스트 A에서 선언한 변수 a를 참조하는 내부함수 B를 A의 외부로 전달할 경우, A가 종료되더라도 a가 사라지지 않는 현상, closure
✅ 즉, 지역변수가 함수 종료 후에도 사라지지 않게 할 수 있다.
💻 다른 예시를 풀어보자 )
function user(_name){
var _logged = true;
return{
get name(){return _name},
set name(v){_name=v},
login(){_logged=true},
logout(){_logged=false},
get status(){
return _logged
? 'login'
:'logout';
},
}
}
var roy = user('재남');
//1
console.log(roy.name); //재남
//2
roy.name="제이 ";
console.log(roy.name); //제이
//3
roy._name='로이';
console.log(roy.name); //제이 -> 반영되지 않음
//4
console.log(roy.status); //login
//5
roy.logout();
console.log(roy.status); //logout
//6
roy.status=true;
console.log(roy.status); //logout
1️⃣ console.log(roy.name);를 반환하려고 하면 roy 객체에는 name 프로퍼티가 없는 대신에 name에 대한 getter는 있음. getter가 호출이 됨. _name은 user함수에서 선언한 변수. 이 _name은 함수가 종료됨과 동시에 함께 사라지는 변수여야 하지만, 종료가 되어도 사라지지 않음. 하지만 return 해주는 과정에서 해당 변수가 계속 사용되어야 하니까 참조 카운트가 0이 아닌 상태. -> 클로저
2️⃣ roy.name에 setter로 name이라는 프로퍼티가 재남에서 제이로 변경.
3️⃣ roy._name='로이'; roy객체에 _name 프로퍼티가 없음. 이 경우, user 함수에 _name 변수에는 어떠한 영향을 줄 수 없음. 그래서 name getter 호출에 대한 결과는 여전히 "제이"
4️⃣ console.log(roy.status);에서는 status 프로퍼티에 _logged를 리턴하고 이때 _logged는 true이니까 'login'이 출력된다. 즉, _logged 변수 역시 클로저에 의해 사라지지 않고, 남아있음.
5️⃣ logout이라는 메소드를 호출하면, 이제는 사라지지 않은 변수(_logged)가 false가 되고, roy.status를 호출하면 logout이 출력됨.
다시 말해, console.log(roy.staus); 에는 logout() 메소드가 실행되고 _logged가 false가 되며 status 프로퍼티에 false가 반영되어 logout이라는 결과 출력하게 되는 것이다.
6️⃣ 하지만, roy.status=true;
console.log(roy.status); 를 하면 getter는 있지만, setter가 없어서 roy.status에 true를 할당해도 똑같이 logout으로 출력됨.
즉, true가 반영되지 않음.
📚 정리
✅ _name, _logged는 함수가 종료되도 사라지지 않고 값을 유지하는 변수
✅ 외부로부터 내부 변수를 보호할 수 있는 기능(캡슐화)
여기서 캡슐화란❓
📌 위에 코드에서 외부에 노출된 status 프로퍼티는 getter로서만 역할을 하며, 실제 _logged 값과는 별개로 문자열(login/logout)만 반환해줌
📌 직접적인 _logged의 변화에 영향을 주는 건 login(), logout() 메소드일 뿐
📌 이것을 캡슐화, 클로저 기능이 외부로부터 내부 변수를 보호할 수 있는 기능을 가짐
'자바스크립트의 정석 🟡' 카테고리의 다른 글
[자바스크립트] callback 정리 ✨ (0) | 2024.01.28 |
---|---|
[자바스크립트] 프로토타입 정리 ✨ (0) | 2024.01.26 |
[자바스크립트] this 정리 ✨ (1) | 2024.01.23 |
자바스크립트 - Destructuring assignment (0) | 2023.09.19 |
자바스크립트 - Function etc (0) | 2023.09.19 |