[JS] 클로저와 렉시컬 환경 그리고 호이스팅

이의섭·2021년 12월 29일
0
post-thumbnail

함수는 어디에도 쓸 수 있다

자바스크립트는 함수 지향 언어입니다. 함수를 동적으로 생성할 수 있고, 생성한 함수를 다른 함수에 인수로 넘길 수 있으며, 생성된 곳이 아닌 곳에서 함수를 호출할 수 도 있기 때문입니다.

클로저는 간단하게 함수 내부에서 함수 외부에 있는 변수에 접근할 수 있다는 것 입니다.

중첩 함수

자바스크립트에서 중첩 함수를 자주 볼 수 있습니다

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()입니다. 이 방식으로 만들어진 함수는 외부 렉시컬 환경으로 함수가 생성된 렉시컬 환경을 참조하는것이 아니라 전역 렉시컬 환경을 참조합니다. 매우 특이합니다.

new Function()은 실무에서 많이 사용된다고 합니다. 가령 코드를 엄청 작성하다가 중간에 어떤 함수 짜는 방법이 생각났을 경우 유용하다고 합니다. 현재 렉시컬 환경을 참조하지 않기때문에 중간에 함수를 추가하더라도 기존 스크립트와 문제없이 상호작용 할 수 있다고 합니다.

profile
사용자 중심 생각하는 프론트엔드 개발자가 되고 싶은..

0개의 댓글