일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 타입스크립트
- 솔리디티
- 클라우드
- next.js
- 파이썬
- 백엔드
- 자바스크립트
- 웹
- 이더리움
- k8s
- 컴퓨터공학
- 쿠버네티스
- 프론트엔드
- kubernetes
- CSS
- JavaScript
- 알고리즘
- react
- node.js
- BFS
- 블록체인
- 이슈
- AWS
- 리액트
- 백준
- es6
- 가상화
- HTML
- docker
- TypeScript
- Today
- Total
즐겁게, 코드
렉시컬 스코프와 클로저 본문
클로저(closure). 분명 한번은 들어봤지만 한번도 직접 써본적은 없는 그 문법입니다.
그런데 정말 없을까요? 오늘은 클로저가 무엇인지 한번 알아봅시다.
let myName = "Chanmin";
function printName() {
console.log(myName); // "Chanmin"
}
printName();
실행 결과로 "Chanmin" 이 출력되는 것은 쉽게 예측할 수 있는데, 함수 안에서 어떻게 바깥의 myName 변수를 사용할 수 있었던 걸까요?
바로 우리가 모르는 사이에 클로저가 사용되었기 때문입니다.
이번에는 조금 복잡한 예시를 들어 보겠습니다.
function outer(outerValue) {
return function inner(innerValue) {
console.log(`outer value : ${outerValue}`);
console.log(`inner value : ${innerValue}`);
};
}
const newFunction = outer("outside");
newFunction("inside");
// outer value : outside
// inner value : inside
outer 라는 함수가 inner 함수를 리턴한 다음 인자로 "inside" 문자열을 준 것 까지는 괜찮았습니다.
그런데 inner 함수는 "outSide" 라는 값을 어떻게 알고 있던 것일까요?
렉시컬 스코프와 유효 범위
함수가 실행되면 내부 렉시컬 환경과 외부 렉시컬 환경이라는 고유한 유효 범위를 갖고, 이를 메모리에 유지합니다.
("고유한" 을 강조한 이유는 아래에서 설명하겠습니다.)
내부와 외부가 가지는 의미대로 내부 렉시컬 환경은 함수 내부에서 사용된 인자라던가 지역 변수를 다루며, 외부 렉시컬 환경은 함수 바깥의 범위를 참조하게 됩니다. 그리고 변수를 사용할 때는 내부 렉시컬 환경을 검색한 후, 찾고자 하는 변수가 없으면 외부 렉시컬 환경으로 이동해 변수를 계속해서 탐색하는 것이 클로저의 원리입니다.
let myName = "Chanmin"; // 함수 외부 -> 외부 렉시컬 환경
function printName() {
console.log(myName); // 함수 내부 -> 내부 렉시컬 환경
}
printName();
이제 이 코드에 어떻게 클로저가 적용된 것인지 감이 오시나요?
printName 함수는 myName 이라는 변수를 출력하려 하지만 함수 내에서는 myName 이라는 변수가 선언된 사실을 알 수 없습니다.
따라서 상위(외부) 스코프로 이동해 myName을 찾기 시작하고, 거기서 let myName = "Chanmin"
을 발견해 사용하기로 한거죠!
function makeCounter() {
let count = 0; // 익명 함수의 외부 렉시컬 환경
return function () {
return count++; // 내부 렉시컬 환경
};
}
let counter = makeCounter();
console.log(counter()) // 0
console.log(counter()) // 1
이번에는 다른 예시를 들어보겠습니다.
counter 라는 변수에는 makeCounter 함수가 리턴한 익명 함수의 참조가 저장되는데요, 저장한 함수를 실행할 때마다 외부 렉시컬 환경에 존재하는 count 변수에 접근합니다.
하지만, 한 가지 중요한 점을 기억해야 합니다.
function makeCounter() {
let count = 0;
return function () {
return count++;
};
}
let counter = makeCounter();
let counter2 = makeCounter(); // counter와 counter2는 서로 다른 렉시컬 스코프를 참조합니다.
console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter2()); // 0
console.log(counter2()); // 1
바로 함수가 실행되어 렉시컬 환경이 생성될 때마다 "고유의" 렉시컬 환경을 갖는다는 것인데요, 따라서 counter 함수와 counter2 함수는 같은 makeCounter 함수에 의해 생성되었을지라도 둘은 다른 환경을 보유한 채로 메모리에 남게 되는 것입니다.
지금까지는 함수 내에서 정의되지 않은 변수를 사용하는 것이 "전역 변수를 사용한 거였겠지" 라고 생각했을 수도 있지만, 이게 가능했던 이유 뒤에는 클로저가 있었기 때문이라는 사실을 꼭 기억해 주세요. 😁
'💬 언어 > Javascript' 카테고리의 다른 글
커링은 어려워 (0) | 2021.02.18 |
---|---|
객체 복사하기 (feat. 로대쉬) (0) | 2021.02.13 |
이터레이터와 이터러블 (0) | 2021.02.04 |
비밀을 지켜라! - 심볼형 프로퍼티 사용하기 (0) | 2021.01.07 |
이벤트의 버블링과 캡처링 이해하기 (0) | 2021.01.03 |