Programing/JavaScript

[JavaScript] 자바스크립트의 스코프와 호이스팅: 개념과 작동 원리

유니얼 2024. 12. 23. 01:39
728x90

JavaScript를 깊이 이해하기 위해서는 스코프(Scope)호이스팅(Hoisting)의 개념을 명확히 이해하는 것이 중요합니다. 이 글에서는 변수의 접근 범위를 정의하는 스코프와, JavaScript의 독특한 변수 처리 방식인 호이스팅에 대해 상세히 설명합니다.

1. 스코프(Scope)

스코프는 변수나 함수가 정의되고 접근할 수 있는 범위를 의미합니다. 특정 코드에서 변수에 접근할 수 있는지는 그 변수의 스코프에 의해 결정됩니다.

스코프의 종류

1. 글로벌 스코프

정의: 함수나 블록 외부에서 선언된 변수는 글로벌 스코프를 가집니다.

특징:

  • 애플리케이션의 모든 코드에서 접근 가능합니다.
  • 글로벌 변수는 어디에서나 접근할 수 있지만, 지나치게 사용하면 다른 코드와 충돌할 가능성이 높아 권장되지 않습니다.

예시:

var globalVar = "I am global";

function printGlobalVar() {
  console.log(globalVar); // "I am global"
}
printGlobalVar();

2. 함수 스코프

정의: 함수 내부에서 선언된 변수는 함수 스코프를 가집니다.

특징:

  • 해당 함수 내부에서만 접근 가능하며, 함수 외부에서는 접근할 수 없습니다.
  • **지역 변수(Local Variable)**라고도 불립니다.

예시:

function myFunction() {
  var localVar = "I am local";
  console.log(localVar); // "I am local"
}
myFunction();
console.log(localVar); // ReferenceError: localVar is not defined

3. 블록 스코프 (ES6 이후)

정의: {}로 묶인 블록 안에서 선언된 변수는 블록 스코프를 가집니다.

특징:

  • let과 const 키워드로 선언된 변수에만 적용됩니다.
  • 블록 내부에서만 접근할 수 있으며, 블록 외부에서는 접근할 수 없습니다.
  • 함수 또한 블록 스코프를 따릅니다.

예시:

if (true) {
  let blockVar = "I am block scoped";
  console.log(blockVar); // "I am block scoped"
}
console.log(blockVar); // ReferenceError: blockVar is not defined

2. 스코프 체인(Scope Chain)

스코프 체인은 특정 변수를 찾기 위해 자바스크립트가 스코프 계층을 탐색하는 과정을 말합니다.

작동 원리:

  • 변수를 참조할 때, 현재 스코프에서 변수를 찾습니다.
  • 만약 현재 스코프에 변수가 없다면, 상위 스코프를 순차적으로 탐색합니다.
  • 글로벌 스코프까지 탐색해도 변수를 찾지 못하면 ReferenceError가 발생합니다.

예시:

var globalVar = "Global";

function outerFunction() {
  var outerVar = "Outer";
  function innerFunction() {
      var innerVar = "Inner";
      console.log(innerVar); // "Inner"
      console.log(outerVar); // "Outer"
      console.log(globalVar); // "Global"
  }
  innerFunction();
}
outerFunction();

 

3. 호이스팅(Hoisting)

호이스팅은 JavaScript에서 변수와 함수 선언이 실행 컨텍스트의 최상단으로 끌어올려지는 동작을 말합니다.

 

- 변수나 함수의 선언이 실행 전에 스코프의 최상단으로 끌어올려지는 것처럼 동작합니다.
- 다만, 실제로 코드를 이동시키는 것은 아니며, 인터프리터가 이를 미리 처리합니다.

호이스팅의 작동 방식

1, var 변수:

  • var로 선언된 변수는 선언과 초기화가 함께 호이스팅됩니다.
  • 초기 값은 undefined로 설정되며, 이후 코드에서 재할당됩니다.

예시:

console.log(a); // undefined
var a = 10;
console.log(a); // 10

 

2, let, const 변수:

  • let과 const로 선언된 변수는 선언과 초기화가 분리되어 처리됩니다.
  • 초기화가 이루어지기 전에 변수를 참조하면 **ReferenceError**가 발생합니다.
  • 이는 일시적 사각지대(TDZ, Temporal Dead Zone) 때문입니다.

예시:

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

 

3, 함수 선언:

  • 함수 선언문은 전체가 호이스팅됩니다. 따라서 선언된 함수는 선언 위치와 상관없이 호출할 수 있습니다.
  • 예시:
greet(); // "Hello!"
function greet() {
   console.log("Hello!");
}

 

4, 함수 표현식:

  • 함수 표현식은 호이스팅되지 않습니다. 선언 전에 호출하면 오류가 발생합니다.

예시:

console.log(greet); // undefined
var greet = function() {
   console.log("Hi!");
};
greet(); // "Hi!"

4. 일시적 사각지대(TDZ)

일시적 사각지대는 변수 선언이 호이스팅되었지만 초기화가 이루어지기 전까지의 구간을 의미합니다.

특징:

  • TDZ에서는 변수를 참조할 수 없으며, 이를 시도하면 ReferenceError가 발생합니다.
  • 변수 선언이 스코프의 시작 지점에서 이루어지더라도 초기화가 이루어지기 전까지는 접근할 수 없습니다.

예시:

{
    console.log(x); // ReferenceError: Cannot access 'x' before initialization
    let x = 10;
    console.log(x); // 10
}

호이스팅이 일어나는 경우와 그렇지 않은 경우

호이스팅이 일어나는 경우

  • var 키워드로 선언된 변수.
  • 함수 선언문.

호이스팅이 일어나지 않는 경우

  • let과 const 키워드로 선언된 변수.
  • 함수 표현식.

 

전체 예제 코드

// 스코프
'strict mode'


// 나이 계산 함수
function calcAge(birthYear) {
    const age = 2025 - birthYear; // 현재 연도에서 태어난 연도를 뺀 값으로 나이 계산

    function printAge() {
        const output = `${firstName}님, 당신은 ${age}살이고 ${birthYear}년도에 태어났습니다.`;
        console.log(output); // 계산된 나이 출력

        if (birthYear >= 1981 && birthYear <= 1996) { // 밀레니얼 세대 여부 확인
            var millenial = true; // 밀레니얼 여부를 저장하는 변수 (var로 선언)

            const firstName = "Kevin"; // if 블록 내 지역 변수
            const str = `${firstName}, 당신은 밀레니얼입니다.`;

            // console.log(str); // 밀레니얼 여부 메시지 출력

            // 블록 내 함수 선언
            function add(a, b) {
                return a + b; // 두 값을 더하는 함수
            }
        }
        // console.log(millenial); // var는 함수 스코프이므로 접근 가능
        // console.log(add(2, 3)); // add 함수 호출 가능 여부 확인
    }

    printAge(); // printAge 함수 호출
    return age; // 계산된 나이를 반환
}

const firstName = 'Owen'; // 글로벌 스코프 변수

calcAge(1990); // 나이 계산 함수 호출


// 변수 선언 방식의 차이
var me = 'Owen'; // var로 선언한 글로벌 변수
let job = 'engineer'; // let으로 선언한 글로벌 변수
const year = 1993; // const로 선언한 글로벌 변수


// 함수 선언 방식
function addDecl(a, b) {
    return a + b; // 함수 선언식 (호이스팅 적용)
}

const addExpr = function (a, b) {
    return a + b; // 함수 표현식 (호이스팅 적용되지 않음)
};

const addAwwor = (a, b) => a + b; // 화살표 함수

if (!numProducts) { // numProducts 값이 falsy면 실행
    deleteShoppingCart(); // 쇼핑카트 삭제 함수 호출
}

var numProducts = 10; // var로 선언된 변수 (호이스팅됨)

function deleteShoppingCart() {
    console.log("모든 상품 삭제"); // 메시지 출력
}

결론

  • 스코프는 변수가 접근할 수 있는 범위를 결정하며, 글로벌, 함수, 블록 스코프로 나뉩니다.
  • 스코프 체인은 변수를 찾기 위해 상위 스코프를 탐색하는 과정입니다.
  • 호이스팅은 선언문을 스코프의 최상단으로 끌어올리는 동작처럼 보이게 만듭니다.
    하지만 초기화 시점과 TDZ의 개념을 이해해야 관련 오류를 방지할 수 있습니다.

JavaScript의 스코프와 호이스팅은 코드의 작동 방식을 이해하는 데 핵심적인 역할을 합니다. 이를 명확히 이해하면 오류를 예방하고 더 효율적인 코드를 작성할 수 있습니다.

반응형