변수/함수 호이스팅

Park sang woo·2024년 1월 23일

함수 작성 방법

  • 함수 선언문 : 함수 선언 + 정의
  • 함수 표현식 : 표현식 = 식별자(변수) + 연산자(=) + 리터럴(함수)

함수 선언

function test(a, b) {
  return a * b;
}

test(5, 6) // 함수 선언

함수 표현

var x = function (a, b) {
  return a * b
}
console.log(x(2,9))


🏷️ 함수 표현식 장점

??






📓 호이스팅

인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것이다.
선언이 코드 실행 보다 먼저 메모리에 저장되는 과정이다.
쉽게 선 선언 한다고 생각하면 된다.

  • 우선순위
    • 변수할당이 함수 선언보다 우선순위
    • 함수 선언이 변수 선언보다 우선순위

🏷️ 함수 호이스팅

test(5, 6) // 30
function test(a, b) {
  return a * b;
}

helloWorld();  // 'Hello World!'
function helloWorld(){
  console.log('Hello World!');
}



console.log(x(2,9))
var x = function (a, b) {
  return a * b
}
// TypeError: x is not a function

helloWorld();  // TypeError: helloWorld is not a function
var helloWorld = function(){
  console.log('Hello World!');
}

함수 선언식은 코드가 실행되기 전에 로드되서 호이스팅이 가능하지만 함수 표현식은 인터프리터가 해당 코드 줄에 도달을 해야 로드하므로 호이스팅이 불가능하다.

그래서 함수표현식의 선언이 호출보다 위에 있으면 정상 출력된다.



🏷️ 변수 호이스팅

console.log(a); // 'undefined'
var a = 3;
console.log(a); // 3

위와 같은 경우 결과값이 undefined가 나온다.
나중에 선언되는 변수를 미리 접근할 수 있다.

다시 상기시키자.

JS는 오직 선언만을 hoist한다. 초기화(할당)은 hoist하지 못한다.

위 코드는 인터프리터에서 다음과 같이 해석한다.

var a;
console.log(a); // 'undefined'
a = 3;
console.log(a); // 3





📓 렉시컬 함수

자바스크립트 엔진은 코드를 실행하기 전에 Lexical Environment 내에 있는 메모리에 함수, 변수 선언을 추가한다고 한다.
"identifier(변수 or 함수)-variable(실제 객체 or 원시값)" 매핑 정보를 가지고 있는 데이터 구조이다.

변수와 함수가 살고있는 공간






📓 var, let, const

🏷️ 변수 생성 3단계

  • 선언 단계 : 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계.
  • 초기화 단계 : 실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계.
  • 할당 단계 : undefined로 초기화된 메모리의 다른 값을 할당하는 단계.

JS 모든 선언에는 호이스팅이 일어난다.
let, const, class의 경우 선언문을 호이스팅이 발생하지 않는 것처럼 동작한다. (참조 에러 발생.)

var 선언한 변수는 선언과 동시에 초기화가 일어난다.
선언 + undefined (undefined로 초기화. 에러 발생 X)

let, const는 선언과 초기화가 분리되어 진행된다.
즉 변수를 위한 메모리 공간이 확보되지 않음. uninitialized로 남아있음.

console.log(text); // 선언 + 초기화 된 상태 -> undefined 출력
var text; // var인 상태 -> 선언 + 초기화
text = 'Hello!'; // 선언 + 초기화 + 할당 된 상태 -> Hello! 출력
console.log(text); // 정상 Hello1 출력

var 키워드는 선언과 함께 undefined로 초기화되어 메모리에 저장되는데 let과 const는 초기화되지 않은 상태로 선언만 메모리에 저장되기 때문이다. (초기화가 된 상태인지 아닌지를 중점으로 보면 된다.)

console.log(text); // 선언만 된 상태, 초기화 안되서 참조 불가능 -> 에러 발생
let text;
// const로 해도 에러 발생

console.log(myName); // 참조 에러
let myName; // 초기화 단계
myName = 'magnus'; // 할당 단계
console.log(myName); // taenami


🏷️ var, let, const 차이

varletconst
재선언 가능 (중복 O)재선언 불가능 (중복 X)재선언 불가능 (중복 X)
재할당 가능재할당 불가능재할당 불가능
선언과 초기화 동시선언과 초기화 분리선언, 초기화, 할당이 전부 동시
함수 레벨 스코프블록 레벨 스코프블록 레벨 스코프


let은 선언 단계와 초기화 단계가 나누어서 진행된다고 했었다. 그래서 초기화 이전에 변수에 접근하면 참조 에러가 발생했다.

위 그림처럼 TDZ(Temporal Dead Zone) 이라는 곳이 있는데 스코프의 시작 지점부터 초기화 시작 지점까지의 구간 또는 변수의 선언과 초기화 사이에 일시적으로 변수 값을 참조할 수 없는 구간이라고 한다.
let의 경우 선언을 해서 호이스팅이 되었지만 이 TDZ 구간에 의해 메모리가 할당 되지 않아 참조 에러가 발생한 것이다.



🏷️ 스코프

스코프는 identifier, 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름(참조 대상 식별자)를 찾기 위한 규칙이다.

  • 전역 스코프 : 어디에든 참조 가능.
  • 지역 스코프 : 함수 블록이 만든 스코프. 함수 자신과 하위 함수에서만 참조 가능.
var x = 'global';

function foo () {
  var x = 'function scope';
  console.log(x);
}

foo(); // function scope
console.log(x); // global

위 코드에서 전역 변수인 x는 어디에든 참조할 수가 있다. foo() 안의 x는 함수 내에서만 참조가 가능하다. 이러한 규칙을 스코프라고 한다.

스코프가 없으면 같은 이름의 식별자끼리 충돌이 발생해 서로 다른 이름으로만 사용해야 한다.



var x = 10;

function foo() {
  x = 100;
  console.log(x);
}
foo(); // 100
console.log(x) // 100

함수 영역에서 전역변수를 참조할 수 있기 때문에 전역 변수의 값도 변경이 된다.



🏷️ 함수 레벨 스코프, 블록 레벨 스코프

함수는 딱 function으로 묶인 함수를 기준으로 한 범위고 블록은 {}을 기준으로 한 범위이다.

function test(){
  var i = 100;
  for(var i = 0; i<5; i++){
    console.log(i);
  }
  
  return i // 블록 스코프이므로 여기의 i는 위 100을 할당한 i이다.
  // var라면 함수 스코프이므로 for 안의 i가 되기 때문에 5가 나옴.
}

test()

var는 함수 레벨 스코프이기 때문에 i의 값이 5가 출력된다.
let, const는 블록 레벨이므로 i의 값이 100이 출력된다.



🏷️ 렉시컬 스코프

var x = 1;

function foo() {
  var x = 10;
  bar(); // 호출
}

function bar() { // 함수 선언 - 전역
  console.log(x);
}

foo(); // 1
bar(); // 1

함수 bar()의 상위 스코프가 무엇인지에 따라 결정된다. Ⅰ. 함수를 어디서 호출했는지에 따른 상위 스코프 결정, Ⅱ. 함수를 어디서 선언했는지에 따른 스코프 결정.
전자라면 bar의 상위 스코프는 foo이고 후자라면 bar의 스코프가 전역이다.

전자의 경우 동적 스코프라 하고 후자를 렉시컬 스코프(정적 스코프)라고 한다. 대부분의 프로그래밍 언어가 렉시컬 스코프를 따른다고 한다.

그래서 어디에 함수를 선언했는지를 보면 된다. 그러므로 bar가 전역에 선언되었고 x의 값이 2번 출력이 된다.






Reference

🔗 호이스팅(hoisting)에 대하여
🔗 호이스팅(Hoisting)이란?
🔗 https://poiemaweb.com/js-scope - 스코프
🔗 https://taenami.tistory.com/87 - let과 const는 호이스팅되지 않는다?? TDZ란

profile
일상의 인연에 감사하라. 기적은 의외로 가까운 곳에 있을지도 모른다.

0개의 댓글