본문 바로가기
Programming/Javascript

[Javascript] 스코프, 호이스팅

by Bam_t 2021. 2. 25.
728x90

스코프는 변수가 스크립트 내부의 어떤 위치에서 참조될 수 있는가에 대한 것입니다.

 

자바스크립트는 세가지 경우의 스코프를 가지고 있습니다.

1. 스크립트 내부 어디에서든 참조가 가능한 글로벌 스코프

2. 정의되어있는 함수 내부에서만 참조가 가능한 로컬 스코프

3. 블록 내부({})에서만 참조가 가능한 블록 스코프

 

스코프 개념은 변수 선언과 밀접한 연관이 있기때문에 변수 선언이 헷갈린다거나 하면 아래 포스트를 참조합시다.

2021/01/28 - [Programming/Javascript] - [Javascript] 변수 선언과 명명 규칙


1. 글로벌 스코프

글로벌 스코프는 어떤 함수 내부에도 들어가있지 않도록 되어있게 선언된 스코프이다. 더 간단히 하면 함수 외부에 선언된 변수라고도 할 수 있습니다.

 

 

2. 로컬 스코프

로컬 스코프는 현재 선언된 함수 내부에서만 참조될 수 있는 스코프입니다. 즉, 함수 외부에선 이 변수를 참조할 수 없다는 것 입니다.

let globalVar = 1;	//글로벌 스코프

function getAdd() {
    let localVar = 2;	//로컬 스코프
    return localVar + globalVar;
}

console.log(getAdd());
console.log(globalVar);
console.log(localVar);	//에러!:로컬 스코프 외부에서 참조시도

예시를 보면 글로벌 스코프로 선언된 'globalVar'은 getAdd()함수내부, 외부 어디서든 참조 할 수 있습니다.

그러나 getAdd()내부의 로컬 스코프인 localVar는 getAdd()함수 외부에서 참조하려고 하자 오류를 발생시켰음을 볼 수 있습니다.

 

 

2 - 2. 글로벌 스코프와 로컬 스코프는 구분된다. (블록 스코프도!)

글로벌 스코프와 로컬 스코프는 엄밀히 구분됩니다. 다음 코드만 보면 변수 선언의 규칙을 생각했을때 둘 다 2를 출력할 것이라고 예상할 수 있습니다.

let variable = 1;

function getValue() {
    let variable = 2;
    return variable;
}

console.log(getValue());
console.log(variable);

분명 변수 선언에서 중복 선언을 허용하지 않거나 나중에 선언된 값으로 덮어 씌워진다고 했었습니다. 그러나 스코프가 다른 경우에는 이름이 같더라도 다른 변수로 간주되어 사용됩니다.

 

추가적으로 변수 선언에 있어서 let(var)명령을 생략할 수 있다고 했었는데, let 혹은 var명령을 사용하지 않고 변수를 선언하게 된다면 무조건 글로벌 스코프로 인식됩니다.

위의 예제에서 let명령만 없앤 예제입니다.

variable = 1;

function getValue() {
    variable = 2;
    return variable;
}

console.log(getValue());
console.log(variable);

즉 로컬 변수를 정의 하기 위해서는 var(let)명령을 필수적으로 사용하여야 합니다. 잠시 뒤에 나오지만 블록 스코프도 이용하기 위해서는 반드시 let명령을 사용하여 선언해야합니다.

 

 

2 - 3. 함수 인수의 스코프

function 함수명(인수){
}

인수는 일단 로컬 스코프로 취급됩니다. 하지만 변수에는 두가지 종류가 있었는데요. 수치를 직접 다루는 기본형과 메모리의 주소를 다루는 참조형이 있었습니다.

인수가 기본형이라면 로컬 스코프로 동작합니다.

그러나 인수가 참조형일때는 값이 담긴 메모리를 직접 참조하기 때문에 글로벌 스코프처럼 동작합니다. 참조형도 로컬스코프로 받아오긴 하지만 결국 메모리값의 주소를 참조하게 되므로 함수 내에서 값 변경을 시도하면 외부의 변수값도 변하게 됩니다.

 

 

3. 블록 스코프와 let명령

블록 스코프는 ES2015에 추가된 새로운 스코프 개념입니다. 이와 함께 변수 선언에 let명령이 등장하게 되었습니다.

블록이란 개념은 여러번 나왔지만 다시 설명하자면  {}중괄호로 묶인 단위입니다.

for(;;){
}

while(1){
}

if(1){
}

function getValue() {
}

//전부 다 블록

 

let명령(상수 선언의 const명령도)은 var에선 지원하지 않던 블록 스코프 단위의 스코프를 지원합니다. 변수 선언에서 let명령을 권장했던 이유가 변수 스코프는 작을수록 좋다 라는 프로그래밍 규칙을 따르기 위해서였습니다. 

 

 

4. 호이스팅과 로컬, 블록 스코프

로컬과 블록 스코프를 다루면서 나온말이 '함수/블록 내부라면 어디에서든 참조할 수 있다.' 였는데 중요한 개념이 하나 있습니다.  이말대로라면 다음 코드의 실행은 2를 두번 출력할 것입니다.

function getValue() {
    console.log(num1);
    let num1 = 2;
    return num1;
}

console.log(getValue());

console.log(num1);에서 참조 오류가 발생했음을 볼 수 있습니다.

원리는 2째줄에서 사실은 num1을 위한 공간이 할당되어 있습니다. 그러나 공간만 할당되어있고 정의와 초기화는 3번째 줄 let명령을 통해 이루어지기때문에 오류가 발생하게되는 것 입니다. 즉, 다음과 같은 상태입니다.

function getValue() {
    console.log(num1);	//undefined
    let num1 = 2;	//number
    return num1;	//number
}

console.log(getValue());

이러한 자바스크립트의 동작을 호이스팅(hoisting)이라고 합니다. 그리고 위와 같은 오류를 막기 위해서 로컬, 블록 변수의 선언은 가급적 함수와 블록의 가장 상단에 하는것이 권장됩니다.

728x90

댓글