TIL1. Javascript의 호이스팅과 클로저

imloopy·2022년 3월 21일
1

Today I Learned

목록 보기
1/56

틀린 부분이 있을 수 있으니, 지적해주시면 도움이 많이 됩니다. 감사합니다.

Today I learned:

오늘은 자바스크립트의 기초를 다지는 시간을 가졌다. 그 중, 자바스크립트의 호이스팅과, 클로저가 잘 이해가 되지 않아 찾아보던 도중, 호이스팅 현상과 클로저에 대하여 이해하려면 자바스크립트 함수의 실행 과정에 대한 전반적인 이해가 필요하다고 느꼈다.

함수의 호출

함수의 호출이란, 함수를 실행하기 위하여 실행 컨텍스트를 생성하고, 생성될때 만들어진 변수와 매개변수, this등을 함수에 전달하여 실행시키는 것을 말한다.
1. 함수를 호출하면 이를 실행할 수 있는 환경을 만들고 초기화한다.
2. this를 바인딩한다.
3. 함수를 실행하고 결과값을 저장한다.
4. 함수가 종료되고 결과값을 반환한다.

자세한 함수의 호출 과정

함수의 호출 과정은 다음 세가지로 나눌 수 있다.
1. PrepareForOrdinaryCall (Execution context 생성)
2. OrdinaryCallBindingThis (This 바인딩)
3. OrdinaryCallEvaluateBody (함수를 실행하고 값을 저장)
우리가 주목할 것은 1번 과정이다.

실행 컨텍스트 (Execution Context)

Execution Context란, 코드를 실행하기 위한 환경 정보들을 담고 있는 객체이며, 스코프와 기본 객체 등 코드를 실행하기 위해 필요한 정보들을 담고 있는 장치라고 볼 수 있다.
Execution Context내에 LexicalEnvironment 객체가 생성되는데, 정확히는 LexicalEnvironment객체가 변수와 함수등의 식별자를 정의하는데 사용되는 객체이다. LexicalEnvironment는 다음 두 가지로 구성되어 있다.

  1. Environment Record: 변수, 함수등의 식별자를 정의하고 값을 저장하는 장소
  2. outer: 외부 Environment Scope를 참조하고, 스코프 체인을 구성
    따라서 현재 envrionment record에 찾는 식별자가 없다면, outer의 lexical environment로 거슬러 올라가 값을 찾으려고 시도한다.

호이스팅이란?

호이스팅이란, 식별자의 선언 부분이 최상단으로 끌어올려지는 현상을 말한다. 물론 물리적으로 끌어올려지는 것은 아니다. 함수의 호출 과정을 다시한번 살펴보면, PrepareForOrdinaryCall에서 Execution Context가 생성되는데, 이 때 LexicalEnvironment 객체에 선언된 식별자들을 저장하기 때문에 상단부에 끌어올려지는 것 처럼 보인다.
따라서 다음의 코드는 문제되지 않는다.

function hello() {
	console.log(a);  // undefined
  	var a = 'hello';
}

상식대로라면 당연히 오류를 내야하지만... 자바스크립트는 그렇지 않다. undefined라는 값을 가진다. LexicalEnvironment 객체에 식별자 정보가 저장되어있기 때문이다. 이러한 동작 방식은 디버깅시에 많은 어려움을 겪게 한다.

let과 const를 쓰자

let과 const 예약어를 사용한다면, 이러한 실수를 저질렀을 때 명확하게 Reference Error를 반환한다. 따라서 let과 const를 쓰자. let과 const는 선언 단계와 초기화 단계가 분리되어 진행된다. 선언 단계가 먼저 실행되지만, 초기화 단계가 실행되지 않았을 때 해당 변수에 접근하려고 하면 에러가 뜬다.

function hello() {
	console.log(a);  // Reference Error
  	const a = 'hello';
}

클로저란?

함수와 함수가 선언되었을 때의 렉시컬 환경의 조합이다.

무슨 뜻인지 잘 이해가 안간다. counter 함수로 예시를 들어보자.

function counter() {
  let count = 0
  return function() {
    return ++count;
  }
}
const increase = counter();
increase();  // 1
increase();  // 2
increase();  // 3
  1. counter 함수는 이미 종료되었기 때문에 call stack에서 제거되었다.
  2. 그러나 increaseLexicalEnvironment는 선언 시에 결정되기 때문에, counter함수를 참조한다.
  3. counter의 내부 함수에서 count가 선언되지 않았으므로, counter의 외부 스코프로 거슬러 올라가 count가 선언되었는지 확인한다. (LexicalEnvironmentEnvironment Recordouter로 구성되어 있고, outer에서 외부 스코프를 참조함)
  4. 외부 스코프인 counter함수에서 count가 선언되었음을 확인했으므로, count를 증가시킨 값을 반환한다.

클로저 사용의 장점

  1. 정보의 은닉화가 가능
  2. 지역 변수의 사용 억제
  3. 현재 상태를 기억하고 변경된 최신 상태를 유지

느낀 점

함수가 호출되고 어떻게 동작하는지 이해하기 위하여 여러가지 자료들을 찾아보았다. 다만, 워낙 내용이 어려워서 시간을 두고 반복해서 읽어봐야겠다 :)

출처

https://www.howdy-mj.me/javascript/var-let-const/
https://poiemaweb.com/js-closure
https://meetup.toast.com/posts/123

1개의 댓글

comment-user-thumbnail
2022년 3월 21일

더 깊게 공부하셨군요! 덕분에 실행 컨텍스트에 대해 훑어보고 갑니다 : )

답글 달기