[자바스크립트] 변수의 유효범위와 클로저
글 작성자: 망고좋아
반응형
변수의 유효 범위와 클로저
- 자바스크립트는 함수 지향 언어이다.
- 함수를 동적으로 생성할 수 있고, 생성한 함수를 다른 함수에 인수에 넘길 수 있으며, 생성된 곳이 아닌 곳에서 함수를 호출할 수 있다.
중첩 함수
function sayHiBye(firstName, lastName) {
// 헬퍼(helper) 중첩 함수
function getFullName() {
return firstName + " " + lastName;
}
alert( "Hello, " + getFullName() );
alert( "Bye, " + getFullName() );
}
- 함수 내부에서 선언한 함수는 ‘중첩(nested)’ 함수라고 부른다.
- 중첩 함수는 새로운 객체의 프로퍼티 형태나 중첩 함수 그 자체로 반환될 수 있다.
- 이렇게 반환된 중첩 함수는 어디서든 호출해 사용할 수 있다.
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2
렉시컬 환경
단계 1. 변수
- 자바스크립트에선 실행 중인 함수, 코드 블록
{...}
, 스크립트 전체는 렉시컬 환경(Lexical Environment) 이라 불리는 내부 숨김 연관 객체(internal hidden associated object)를 갖는다.
렉시컬 환경 객체 구성
- 환경 레코드
- 모든 지역 변수를 프로퍼티로 저장하고 있는 객체
this
값과 같은 기타 정보도 여기에 저장된다.
- 외부 렉시컬 환경에 대한 참조
- 외부 코드와 연관됨
- ’변수’는 특수 내부 객체인
환경 레코드
의 프로퍼티일 뿐입니다. '변수를 가져오거나 변경’하는 것은 '환경 레코드의 프로퍼티를 가져오거나 변경’함을 의미한다.
- 위 두 줄짜리 코드엔 렉시컬 환경이 하나만 존재한다.
- 이렇게 스크립트 전체와 관련된 렉시컬 환경은 전역 렉시컬 환경이라고 한다.
- 네모 상자는 변수가 저장되는 환경 레코드, 화살표는 외부 렉시컬 환경에 대한 참조, 전역 렉시컬 환경은 외부 참조를 갖지 않기 때문에 화살표가
null
을 가리킨다.
- 스크립트가 시작되면 스크립트 내에서 선언한 변수 전체가 렉시컬 환경에 올라간다.
- 변수의 상태는 특수 내부 상태인 'uninitialized’가 된다.
- 자바스크립트 엔진은 'uninitialized’ 상태의 변수를 인지하지만,
let
을 만나기 전까지 이 변수를 참조할 수 없다.
let phrase
두두등장. 아직 값을 할당하기 전이기 때문에 프로퍼티 값은undefined
.phrase
는 이 시점 이후부터 사용 가능phrase
에 값이 할당phrase
의 값이 변경
중간 정리
- 변수는 특수 내부 객체인 환경 레코드의 프로퍼티이다.
- 환경 레코드는 현재 실행 중인 함수와 코드 블록, 스크립트와 연관되어 있다.
- 변수를 변경하면 환경 레코드의 프로퍼티가 변경된다.
⚠️ 렉시컬 환경은 명세서에만 존재
- 이론상의 객체이므로 렉시컬 환경을 얻거나 조작하는 것은 불가능하다!(휴! 다행이다!)
단계 2. 함수 선언문
- 함수 선언문(function declaration)으로 선언한 함수는 일반 변수와는 달리 바로 초기화된다는 점에서 변수와 차이가 있다.
- 함수 선언문으로 선언한 함수는 렉시컬 환경이 만들어지는 즉시 사용할 수 있다.
- 선언되기 전에도 함수를 사용할 수 있는 것은 이 때문이다.
let say = function(name)...
같이 함수를 변수에 할당한 함수 표현식(function expression)은 해당되지 않는다.
단계 3. 내부와 외부 렉시컬 환경
- 함수를 호출해 실행하면 새로운 렉시컬 환경이 자동으로 만들어진다.
- 이 렉시컬 환경엔 함수 호출 시 넘겨받은 매개변수와 함수의 지역 변수가 저장된다.
- 내부 렉시컬 환경은 실행 중인 함수인
say
에 상응 - 내부 렉시컬 환경엔 함수의 인자인
name
으로부터 유래한 프로퍼티 하나가 있다.say("John")
을 호출했기 때문에,name
의 값은"John"
이 된다. - 외부 렉시컬 환경은 전역 렉시컬 환경, 전역 렉시컬 환경은
phrase
와 함수say
를 프로퍼티로 갖는다.
- 내부 렉시컬 환경은 외부 렉시컬 환경에 대한 참조를 갖는다.
- 코드에서 변수에 접근할 땐, 먼저 내부 렉시컬 환경을 검색 범위로 잡는다.
- 내부 렉시컬 환경에서 원하는 변수를 찾지 못하면 검색 범위를 내부 렉시컬 환경이 참조하는 외부 렉시컬 환경으로 확장한다.
- 이 과정은 검색 범위가 전역 렉시컬 환경으로 확장될 때까지 반복된다.
변수 검색 진행 방식
- 함수
say
내부의alert
에서 변수name
에 접근할 땐, 먼저 내부 렉시컬 환경을 살펴본다. 내부 렉시컬 환경에서 변수name
을 찾았다. alert
에서 변수phrase
에 접근하려는데,phrase
에 상응하는 프로퍼티가 내부 렉시컬 환경엔 없다. 따라서 검색 범위는 외부 렉시컬 환경으로 확장된다. 외부 렉시컬 환경에서phrase
를 찾았다.
단계 4. 함수를 반환하는 함수
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
makeCounter()
를 호출하면 호출할 때마다 새로운 렉시컬 환경 객체가 만들어지고 여기에makeCounter
를 실행하는데 필요한 변수들이 저장된다.makeCounter()
를 호출할 때도 두 개의 렉시컬 환경이 만들어진다.makeCounter()
가 실행되는 도중엔 본문(return count++
)이 한 줄짜리인 중첩 함수가 만들어진다는 점이 위쪽에서 살펴본say("John")
과 다르다.- 현재는 중첩 함수가 생성되기만 하고 실행은 되지 않은 상태이다.
중요!
- 모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다는 점이다.
- 함수는
[[Environment]]
라 불리는 숨김 프로퍼티를 갖는데, 여기에 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장된다.
- 따라서
counter.[[Environment]]
엔{count: 0}
이 있는 렉시컬 환경에 대한 참조가 저장된다. [[Environment]]
프로퍼티 덕분에 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 이유이다.[[Environment]]
는 함수가 생성될 때 딱 한 번 값이 세팅되고 영원히 변하지 않는다.counter()
를 호출하면 각 호출마다 새로운 렉시컬 환경이 생성된다.- 이 렉시컬 환경은
counter.[[Environment]]
에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조한다.
- 중첩 함수에서
count
변수가 필요한데, 먼저 자체 렉시컬 환경에서 변수를 찾는다. - 익명 중첩 함수엔 지역 변수가 없기 때문에 렉시컬 환경은 비어있다.
- 그래서
count()
.의 렉시컬 환경이 참조하는 외부 렉시컬 환경에서count
를 찾는다. - 그다음
count++
가 실행되면서 count 값이 1 증가해야 하는데, 변숫값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄진다. - 실행 종료 후 상태는 아래와 같다.
클로저
- 클로저는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미한다.
- 자바스크립트의 함수는 숨김 프로퍼티인
[[Environment]]
를 이용해 자신이 어디서 만들어졌는지를 기억한다. 함수 본문에선[[Environment]]
를 사용해 외부 변수에 접근한다.
가비지 컬렉션
- 함수 호출이 끝나면 함수에 대응하는 렉시컬 환경이 메모리에서 제거된다.
- 자바스크립트에서 모든 객체는 도달 가능한 상태일 때만 메모리에 유지된다.
- 하지만 호출이 끝난 후에도 여전히 도달 가능한 중첩 함수가 있을 수 있다.
- 이때는 이 중첩 함수의
[[Environment]]
프로퍼티에 외부 함수 렉시컬 환경에 대한 정보가 저장된다. -> 도달 가능한 상태
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // g.[[Environment]]에 f() 호출 시 만들어지는
// 렉시컬 환경 정보가 저장된다.
- 렉시컬 환경 객체는 다른 객체와 마찬가지로 도달할 수 없을 때 메모리에서 삭제된다.
- 해당 렉시컬 환경 객체를 참조하는 중첩 함수가 하나라도 있으면 사라지지 않는다.
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // g가 살아있는 동안엔 연관 렉시컬 환경도 메모리에 살아있다.
g = null; // 도달할 수 없는 상태가 되었으므로 메모리에서 삭제
📌 참고
반응형
'프로그래밍 > JavaScript' 카테고리의 다른 글
[자바스크립트] JSON.stringify(value), JSON.parse() (0) | 2021.07.13 |
---|---|
[자바스크립트] 타이머 관련 메소드(setTimeout, clearTimeout, setInterval), 중첩 setTimeout (0) | 2021.07.12 |
[자바스크립트] Array.from(), 배열로 만들어주기 (0) | 2021.07.10 |
[자바스크립트] 날씨 정보 가져오기, Geolocation API (0) | 2021.07.09 |
[자바스크립트] 노드의 관리, appendChild(), createElement(), createAttribute() 등 (0) | 2021.07.08 |
댓글
이 글 공유하기
다른 글
-
[자바스크립트] JSON.stringify(value), JSON.parse()
[자바스크립트] JSON.stringify(value), JSON.parse()
2021.07.13 -
[자바스크립트] 타이머 관련 메소드(setTimeout, clearTimeout, setInterval), 중첩 setTimeout
[자바스크립트] 타이머 관련 메소드(setTimeout, clearTimeout, setInterval), 중첩 setTimeout
2021.07.12 -
[자바스크립트] Array.from(), 배열로 만들어주기
[자바스크립트] Array.from(), 배열로 만들어주기
2021.07.10 -
[자바스크립트] 날씨 정보 가져오기, Geolocation API
[자바스크립트] 날씨 정보 가져오기, Geolocation API
2021.07.09