일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 주식
- algorithmStudy
- 알고리즘공부
- 경제
- algorithmTest
- 알고리즘 공부
- algorithmtraining
- 성분
- 독후감
- 서평
- Java
- 지혜를가진흑곰
- 재테크
- 독서
- C
- 알고리즘트레이닝
- 화장품
- 투자
- 돈
- 자바스크립트
- 자바
- 책을알려주는남자
- 프로그래머스 알고리즘 공부
- JavaScript
- 프로그래밍언어
- 백준알고리즘
- C++
- 다독
- 채권
- 책알남
- Today
- Total
탁월함은 어떻게 나오는가?
[JavaScript] 클로저(Closure),렉시컬 스코프(Lexical Scoping)이란 무엇일까? 사용 예제와 원리를 알아보자 본문
[JavaScript] 클로저(Closure),렉시컬 스코프(Lexical Scoping)이란 무엇일까? 사용 예제와 원리를 알아보자
Snow-ball 2021. 12. 1. 17:16클로저(closure)는 자바스크립트를 사용하다보면 자주 접하게되는 용어이다. 하지만, 클로저는 자바스크립트 고유의 개념은 아니다. 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이다.
MDN에서 정의하는 클로저를 알아보자.
클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
중요한점은 어휘적 범위(Lexical scoping)이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
const num = 1;
function outerFunction() {
const num = 10;
function innerFunction() {
console.log(num) // 10
}
innerFunction();
}
outerFunction();
|
cs |
중첩 함수 innerFunction 내부에서 자신을 포함하고 있는 외부 함수 outerFunction의 number변수에 접근 할 수 있다.
만약 innerFunction 함수가 outerFunction 함수의 내부에서 정의된 중첩 함수가 아니라면 innerFunction 함수를 outerFunction 함수의 내부에서 호출한다고 하더라도 outerFunction 함수의 변수에 접근할 수 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
const num = 1;
function outerFunction() {
const num = 10;
innerFunction();
}
function innerFunction() {
console.log(num); // 1
}
outerFunction();
|
cs |
이러한 현상은 자바스크립트가 렉시컬 스코프를 따르기 때문이다.
렉시컬스코프란?
함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다. 함수가 호출된 위치는 상위 스코프 결정에 어떠한 영향도 주지 않는다. 즉, 함수의 상위 스코프는 언제나 자신이 정의된 스코프다.
이처럼 함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정된다. 함수 정의(함수 선언문 또는 함수 표현식)가 실행되어 생성된 함수 객체는 이렇게 결정된 상위 스코프를 기억한다. 함수가 호출될 때 마다 함수의 상위 스코프를 참조할 필요가 있기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
const x = 1;
function foo() {
const x = 10;
bar();
}
function bar() {
console.log(x);
}
foo();
// 1
bar();
// 1
|
cs |
위 예제에서 확인이 가능하듯 함수의 상위 스코프는 함수를 정의한 위치에 의해 정적으로 결정되고 변경되지 않는다. 렉시컬 환경은 자신의 "외부 렉시컬 환경에 대한 참조(Outer Environment Reference)"를 통해 상위 렉시컬 환경과 연결된다. 이것이 스코프 체인이다.
"함수의 상위 스코프를 결정한다"는 "렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다"는 것과 같다. 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값이 바로 상위 렉시컬 환경에 대한 참조이며, 이것이 상위 스코프이기 때문이다.
정리하자면, 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값. 즉, 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다. 이것이 렉시컬 스코프이다.
클로저와 렉시컬 환경 조합
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
const x = 1;
function outer() {
const x = 10;
const inner = function () {
console.log(x);
};
return inner;
}
// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝(pop)되어 제거된다.
const innerFunction = outer();
innerFunction(); // 10
|
cs |
outer()를 호출하면 outer()는 중첩 함수 inner를 반환하고 생명 주기(life cycle)를 마감한다.
outer()의 지역 변수 x와 변수 값 10을 저장하고 있던 outer 함수의 실행 컨텍스트가 제거되었으므로 outer() 지역 변수 x 또한 생명 주기를 마감하게 된다. 그러나 위 코드의 실행 결과를 확인하면 outer()의 지역 변수 x의 값인 10이 출력되는 것을 확인할 수 있다.
이것이 가능한 이유는 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있기 때문이다. 이러한 중첩 함수를 클로저(closure)라고 부른다.
자바스크립트의 모든 함수는 자신의 상위 스코프를 기억한다. ([[Environment]] 내부 슬롯에 저장)
모든 함수가 기억하는 상위 스코프는 함수를 어디서 호출하든 상관없이 유지된다. 따라서 함수를 어디서 호출하든 상관없이 함수는 언제나 자신이 기억하는 상위 스코프의 식별자를 참조할 수 있으며 식별자에 바인딩 된 값을 변경할 수도 있다.
추가적으로 클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.
'[Snow-ball]프로그래밍(컴퓨터) > 자바스크립트(JavaScript)' 카테고리의 다른 글
[Java Script] reduce() 베열 및 데이터 변환으로 사용하는 방법 (0) | 2022.01.04 |
---|---|
[JavaScript] Number() 메서드를 함부로 사용하면 안된는 이유와 예시 (0) | 2021.12.20 |
[JavaScript] 해체 할당(구조 분해 할당)으로 객체 속성에 접근하는 방법 (0) | 2021.10.28 |
[JavaScript] 매개변수 기본값(Default Parameters)을 정확하게 사용하는법과 생성하는법 (0) | 2021.10.16 |
[JavaScript] 펼침 연산자(...)로 배열을 복사해야하는 이유 (0) | 2021.10.08 |