뚝딱햄 탈출기

[기술면접대비][JS] 호이스팅의 발생 원인 : 선언자, 스코프 본문

Programming language/JavaScript

[기술면접대비][JS] 호이스팅의 발생 원인 : 선언자, 스코프

hyrmzz1 2024. 3. 13. 17:49

선언자

📌 let, var는 ES6에서 도입된 선언자!

const, let, var

const는 재선언, 재할당 금지. 따라서 변하지 않는 상수를 선언할 때 사용.

let은 재선언 금지, 재할당 가능.

var는 재선언 가능, 재할당 가능.

let a = b;
a = c;	// 재할당. let으로 선언한 a에 사용 가능.
let a = e;	// 재선언. let으로 선언한 a에 사용 불가.

 

2023.08.07 - [Programming language/JavaScript] - [JavaScript] 자바스크립트 변수 : const, let, var

 

[JavaScript] 자바스크립트 변수 : const, let, var

변수 선언 변수 선언이란 데이터를 담는 공간을 생성하는 것이고, 변수 초기화란 생성된 변수에 데이터를 전달하는 것이다. var hello;// 변수 선언 hello = "hello";// 변수 초기화 var hello = "hello";// 변

hyrmzz1.tistory.com


스코프

스코프란 변수에 접근할 수 있는 범위를 뜻한다.

💡 const와 let은 블록 스코프, var는 함수 스코프이다.

함수 스코프

  • 원래 JS에서는 함수 스코프를 따른다.
  • 함수가 선언되면 무조건 스코프가 생성된다.
  • 함수 내에서 선언된 변수는 해당 함수 내에서만 접근 가능하다.

예시 1

if (5 > 4) {
	var secret = '12345';
}

secret // '12345'
  • 어디에서나 변수 secret에 접근할 수 있는 이유는, 함수가 선언되어 있지 않기 때문.
    • 함수가 선언되어야 스코프가 생성된다.
  • 새로운 스코프가 형성되지 않아 변수는 동일한 실행 컨텍스트 내에 존재한다.

예시 2

function a() {
	var secret = '12345';
}

secret; // ReferenceError
  • 함수 a의 생성과 동시에 새로운 실행 컨텍스트가 생성되어 이 실행 컨텍스트 내부에 존재하는 변수 환경에 변수 secret가 저장된다.
  • 부모 스코프는 자식 스코프에 간섭할 수 없다. (= 접근 불가)
  • 따라서 함수 외부(global scope)에서는 함수 a 내부의 변수에 접근할 수 없다.

블록 스코프

  • {}이 생성될 때마다 새로운 스코프가 형성된다.
  • 블록 내에서 선언된 변수는 해당 블록 내에서만 접근할 수 있다.
  • 선언 이전에 접근하면 참조 오류를 발생시킨다. (코드 명확성, 안정성 측면에서 더 좋음)

예시 1

function loop() {
	for(var i = 0; i < 5; i++) {
		console.log(i);
	}
	console.log('final',i);
}

loop();    // 잘 출력됨.
  • for문에서 변수 i를 var로 선언 ⇒ 함수 스코프를 따름 ⇒ for문 내부와 외부에서 모두 변수 i에 접근해 console.log 실행
  • loop이라는 함수 스코프 안에 존재 ⇒ for문 안과 밖에서 i에 접근 가능

예시 2

function  loop() { 
	// let i;  // 이렇게 하면 가능
	for (let i = 0; i < 5; i++) { 
		console.log(i); 
	} 
	console.log('final', i); // for 루프 종료되면 변수 i 참조 불가
} 

loop(); /* ReferenceError: i is not defined */
  • for문 내에서 변수 i를 let으로 선언 ⇒ 변수 i는 for문 내에서만 종속되어 for문 외부에서 i에 접근할 수 없음.

예시 3

function test() {
  console.log(x); // ReferenceError: x is not defined (선언 전에 써서)
  let x = 10;
}

test();

📌 let 키워드는 선언 이전에 변수에 접근하면 참조 에러가 발생하는 이유? “아직 초기화되지 않아서”
📌 var 키워드는 선언 이전에 변수에 접근하면 undefined가 뜨는 이유는? “호이스팅”

호이스팅이란?

  • 변수 선언과 초기화가 분리되어 선언이 가장 위로 끌어올려지는 현상
  • JS의 모든 선언에는 호이스팅이 일어난다.
    • JS의 변수 선언은 선언 → 초기화 단계.
      • 선언 단계
        • 변수명 등록해 JS 엔진에 변수의 존재를 알림.
        • JS 엔진 구동시 선언문을 가장 최우선으로 해석
      • 초기화 단계
        • 값을 저장하기 위한 메모리 공간 확보, 암묵적으로 undefined를 할당해 초기화.
        • 런타임 과정에 이루어짐.
    • var a = 2
      • var a;와 a = 2;로 분리됨.
  • 자바스크립트 엔진은 런타임 이전에 실행 컨텍스트를 생성하는데, 이 때 실행 할 코드를 읽어서 코드를 실행하는데 필요한 변수나 함수를 메모리에 등록한다.
    • JS 엔진은 코드 실행 전 실행 가능한 코드를 형상화하고 구분하는 과정을 거치는데, 이 때 모든 선언(변수, 함수, 클래스)을 스코프에 등록한다.
  • 따라서 선언의 위치에 상관없이 함수/변수를 참조할 수 있는 것이다.
  • 변수가 함수 내에서 정의되었을 경우 선언이 함수의 최상위로 변경되고, 함수 바깥에서 정의되었을 경우 전역 컨텍스트의 최상위로 변경된다.

var의 호이스팅

💡 결론 - 초기화 전에도 참조 가능하나, 초기화 이전이므로 undefined이다.

 

  • var 키워드로 선언 및 초기화를 한 변수는 호이스팅이 발생하면 선언과 거의 동시에 undefined로 초기화된다.
  • 실행 시점의 스코프 최상단에서 해당 변수에 대한 메모리가 살아있기 때문에 선언부 위치에 상관없이 참조, 할당이 가능하다.
    • JS 엔진 구동시 선언문을 가장 최우선으로 해석
function test() {
  console.log(x); // undefined (선언 이전에도 함수 스코프에 존재하는 것으로 인식됨.)
  var x = 10;
}

test();

let, const의 호이스팅

💡  결론 - 호이스팅 후 초기화 전까지 TDZ에 있으므로 ReferenceError가 발생한다.
  • 호이스팅이 되지 않는 것처럼 동작한다.
    • JS의 모든 선언에는 호이스팅이 일어난다.
  • let, const 키워드로 선언 및 초기화를 한 변수는 호이스팅이 발생하면 선언만 이루어지고, 실행 시점에서 실질적인 선언부를 만날 때까지 초기화는 이루어지지 않는다.
    • 선언과 초기화 단계가 분리되어 진행된다.
    • 초기화는 런타임에 실행
      • 초기화 단계에 메모리가 할당된다
  • 따라서 초기화 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.
    • 선언 이후~초기화 이전의 간극을 TDZ(Temporal Dead Zone, 일시적 사각지대)라고 한다. (변수 존재, 초기화 X)
      • 이 간극만큼 해당 변수에 대한 메모리가 존재하지 않아 선언부 상단에서 참조 할당이 불가능하다.

함수 호이스팅 주의사항

  • 함수 표현식은 호이스팅되지 않는다. 
func(); var func = function() {} // 변수 func의 호이스팅 발생 -> reference error 안일어남 // 함수가 undefined이므로 TypeError 발생
  • 변수 선언문보다 함수 선언문이 먼저다.
    • 동일한 이름으로 함수 선언문과 변수 선언문(= 함수표현식)이 있다면 함수 선언문의 호이스팅이 먼저 이루어진다.
func();
var func = function(){ console.log('변수 호이스팅') }
function func() {
	  console.log('함수 호이스팅');
}
// 함수 호이스팅

var의 단점은? 전역변수가 좋지 않은 이유는?

var 단점

  1. 함수 스코프를 가지나 호이스팅 현상으로 인해 함수 밖에서도 접근 가능
    1. 선언부 이전에 참조 가능
    2. 따라서 var 키워드로 선언된 변수를 전역 변수라고 볼 수 있음.
  2. 블록 스코프 미지원
    1. strict mode를 사용하면 var 키워드로 선언된 변수도 블록 스코프를 가진다.
  3. 재선언 가능

전역 변수의 단점

  • 암묵적 결합
    • 모든 코드가 전역 변수 참조 및 변경 가능
    • 어떤 함수가 어떤 전역변수를 사용하는지 파악하기 어렵다.
    • ⇒ 명확성 저하
  • 긴 생명주기
    • 메모리 리소스를 오랜 기간 소비
  • 스코프 체인 상의 종점에 존재
    • 전역 변수의 검색 속도가 가장 느리다
  • 네임 스페이스 오염
    • 여러 파일에서 같은 이름의 전역 변수 사용시 이름 충돌 발생 가능
Comments