자바스크립트는 함수 지향 언어 입니다. 그렇기 때문에 함수를 동적으로 생성
, 생성한 함수를 다른 함수로 인자
로 넘길 수 있으며, 생성한 곳이 아닌 곳에서 함수를 호출
할 수 있습니다.
{...}
코드블록 안에서 선언한 변수는 블록 안에서만 사용 가능합니다.
{
let message = "안녕하세요."; // 블록 내에서만 변숫값을 얻을 수 있습니다.
alert(message); // 안녕하세요.
}
alert(message); // ReferenceError: message is not defined
블록이 없다면 이미 선언된 변수와 동일한 이름을 가진 변수 선언시 에러 발생!
함수 내부에서 선언한 함수를 중첩함수
라고 부릅니다.
function makeCounter() {
let count = 0;
// 중첩함수
return function() {
return count++;
};
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2
🧐 위 함수를 보다보면 드는 의문이 있습니다.
만약 counter()을 여러개 선언하였을때 이 함수들은 서로 독립적인지?
함수와 중첩함수 내 count 변수엔 어떤 값이 할당되는지?
이런 의문을 해결하기 위해서 우리는 렉시컬 환경을 알 필요가 있습니다.
호랑이굴이라고도 불리는 렉시컬환경굴로 들어가 봅시다 🥲
자바스크립트에선 실행중인 함수
, 코드블록{...}
, 스크립트 전체
는 렉시컬환경이라는 내부 숨긴연관객체를 갖습니다.
렉시컬 환경변수는 크게 두 부분으로 구성됩니다.
1. 환경레코드 : 모든 지역변수
를 프러퍼티로 저장하고 있는 객체로, this
값이 여기에 저장된다.
2. 외부 렉시컬 환경 : 외부 코드
와 연관되어 있는 것이 저장 된다.
변수 라는 말은 특수 내부 객체인 환경 레코드의 프로퍼티 중 하나일뿐,
변수를 가져오고 변경하는 것은 환경 레코드의 프로퍼티를 가져오거나 변경
함을 의미합니다.
📌 렉시컬 환경의 예시
환경레코드는 실행중인 함수와 코드블록, 스크립트와 연관되어 있습니다.
변수를 변경하면 환경 레코드의 프로퍼티가 변경 됩니다.
🚧 코드를 사용해 직접 렉시컬 환경을 얻거나 조작하는 것은 불가능합니다.
함수도 변수와 같이 값입니다.
다만 함수 선언문은 일반 변수와 다르게 렉시컬환경에서 바로 초기화 된다는 점에서 차이가 있습니다.
함수 선언문으로 선언한 함수는 렉시컬 환경이 만들어지는 즉시 사용가능합니다.
(변수는 let을 만나 선언되기 전까지 사용 할 수 없다)
선언되기전 함수를 사용할 수 있다는 말이 바로 이것 때문이지요!
🚧 단, let say = function(name)...같이 함수를 변수에 할당한 함수 표현식(function expression)은 해당하지 않습니다.
함수를 호출
해 실행하게 되면 새로운 렉시컬 환경이 자동으로 만들어지게
되는데, 이 렉시컬 환경엔 함수 호출시 넘겨받은 매개변수와 함수의 지역변수가 저장
됩니다.
예를들어 say("john")을 호출하면
위 예시와 같이 내부 렉시컬 환경과 외부 렉시컬 환경 동시에 갖게 됩니다.
실행중인 함수 say
에 상응하게 되며, phrase
와 함수 say
두가지를 프로퍼티로 갖습니다.내부 렉시컬 환경을 검색 범위
로 잡습니다.외부 렉시컬 환경으로 확장
합니다.반복
됩니다.때문에 우리는 ${phrase} 와 ${name} 변수를 찾을 수 있게 되는 것 💥
🚧 전역 렉시컬 환경에 도달할 때까지 변수를 찾지 못하면 엄격 모드에선 에러가 발생합니다.
다시 돌아와 중첩함수에서 가진 의문을 해결해 봅시다.
say("john") 예시처럼 함수 makeCounter()
를 호출하면 2개의 새로운 렉시컬 환경이 만들어집니다.
모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억합니다.
함수가 생성될때 [[Environment]]라 불리는 숨긴 프로퍼티를 갖게 되고, 여기에 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장 되는 것이죠.
따라서 counter.[[Environment]]엔 { count:0 }이 있는 렉시컬환경에 대한 참조가 저장됩니다.
호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 [[Environment]] 덕분이라 할 수 있습니다
[[Environment]]는 한번 값이 세팅되고 나면 영원이 변하지 않는 특징을 갖습니다.
외부에서 호출된 counter()은 자체 렉시컬 환경 변수
에서 변수
를 찾고, 없을 경우 외부렉시컬 환경을 참조
하게 되는데 위 중첩함수엔 지역변수가 없이 때문에 렉시컬환경은 <empty>
상태이며, counter()의 외부 렉시컬 환경을 참조
하게 되며 count
를 찾을 수 있는 것입니다!
이때 count 변수를 외부에서 참조
하여 변경된 변수를 저장된 렉시컬 환경
에서 이뤄지기 때문에
counter()을 여러번 호출했을때 count 변수가 2,3 으로 증가 할 수 있게 된것입니다.
클로저(closure)는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미합니다.
자바스크립트의 함수는 숨김 프로퍼티인 [[Environment]]를 이용해 자신이 어디서 만들어졌는지를 기억합니다. 함수 본문에선 [[Environment]]를 사용해 외부 변수에 접근합니다.