함수는 어디에도 쓸 수 있다
자바스크립트는 함수 지향 언어입니다. 함수를 동적으로 생성할 수 있고, 생성한 함수를 다른 함수에 인수로 넘길 수 있으며, 생성된 곳이 아닌 곳에서 함수를 호출할 수 도 있기 때문입니다.
클로저는 간단하게 함수 내부에서 함수 외부에 있는 변수에 접근할 수 있다
는 것 입니다.
자바스크립트에서 중첩 함수를 자주 볼 수 있습니다
function sayHiBye(firstName, lastName) {
function getFullName() {
return firstName + " " + lastName
}
console.log("Hello, " + getFullName());
console.log("Bye, " + getFullName());
}
위 예시처럼 함수 내부에서는 함수 외부에 있는 변수(firstName
, lastName
)에 접근할 수 있다는걸 보실 수 있습니다.
또한
function makeCounter() {
let count = 0;
return function() {
return count++;
}
}
let counter_1 = makeCounter();
console.log(counter_1()); // 0
console.log(counter_1()); // 1
console.log(counter_1()); // 2
let counter_2 = makeCounter();
console.log(counter_2()); // 0
console.log(counter_2()); // 1
이처럼 makeCounter()
결과를 새로운 변수에 담으면 각각의 count가 독립적으로 존재하는것을 확인하실 수 있습니다. 어떻게 해서 이런 결과가 나오는걸까요?
자바스크립트에선 실행 중인 함수, 코드 블록, 스크립트 전체(자바스크립트 파일 자체
)는 모두 렉시컬 환경(Lexical Enviroment
)이라는 내부 숨김 연관 객체를 갖습니다.
이 렉시컬 환경은 두 부분으로 구성됩니다.
1.
환경 레코드 : 모든 지역 변수를 프로퍼티로 저장하고 있는 객체입니다.this
값과 같은 기타 정보도 여기에 저장됩니다.
2.
외부 렉시컬 환경에 대한 참조 : 외부 코드와 연관됨
우리가 전역으로 선언한 변수들은 사실 이 특수 내부 객체인 환경 레코드
의 프로퍼티일 뿐입니다. 때문에 변수를 호출하는 것은 환경 레코드
의 프로퍼티를 가져오는것 입니다.
호이스팅
도 이 환경 레코드
에 저장되기때문에 일어나는 현상입니다. 스크립트가 시작되면 스크립트 내에서 선언한 변수 전체가 렉시컬 환경에 올라가기 때문입니다.
자바스크립트 엔진은 var
로 선언된 변수의 경우는 이 과정에서 uninitialized
상태의 변수를 undefined
로 참조하고 const
,let
으로 선언된 변수는 그 줄을 읽을때까지 uninitialized
상태의 변수를 참조할 수 없기때문에 선언전에 할당을 하면 에러가 발생하는 것 입니다.
함수
도 호이스팅이 된다는 사실을 알고 계실겁니다. 함수 선언문의 경우는 일반 변수와는 달리 var
로 선언한 변수와 마찬가지로 바로 초기화된다는 점
에서 차이가 있습니다. 환경 레코드에서 바로 function
이라는 값으로 초기화 시켜줍니다. 때문에 함수 선언문으로 선언한 함수는 렉시컬 환경이 만들어지는 즉시 사용할 수 있는것
입니다.
함수를 호출해 실행하면 새로운 렉시컬 환경이 자동으로 만들어집니다. 이 렉시컬 환경엔 함수 호출 시 넘겨받은 매개변수
와 함수의 지역 변수
가 저장됩니다. 그리고 함수를 호출한 곳을 이 새로운 렉시컬 환경
의 외부 렉시컬 환경
으로 참조하게 됩니다.
모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억하기때문에 이게 가능합니다. 생성된 곳의 렉시컬 환경은 함수의 [[Environment]]
라는 프로퍼티에 저장됩니다. 호출 장소에 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 이 [[Environment]]
프로퍼티 때문입니다.
이 프로퍼티는 생성될 때 딱 한 번 값이 세팅되고 영원히 변하지 않습니다.
때문에 위의 예시에서 makeCounter
이 실행되면 count: 0
프로퍼티를 갖는 새로운 (1)
렉시컬 환경이 생기고, counter_1
이 실행되면 (1)
렉시컬 환경을 참조하는 새로운 렉시컬 환경이 만들어지기때문에 독립적으로 값을 증가시키는 이유를 알 수 있겠습니다.
함수 생성방법 중 하나인 new Function()
입니다. 이 방식으로 만들어진 함수는 외부 렉시컬 환경
으로 함수가 생성된 렉시컬 환경
을 참조하는것이 아니라 전역 렉시컬 환경을 참조합니다. 매우 특이합니다.
new Function()
은 실무에서 많이 사용된다고 합니다. 가령 코드를 엄청 작성하다가 중간에 어떤 함수 짜는 방법이 생각났을 경우 유용하다고 합니다. 현재 렉시컬 환경을 참조하지 않기때문에
중간에 함수를 추가하더라도 기존 스크립트와 문제없이 상호작용 할 수 있다고 합니다.