전역과 지역 스코프, 호이스팅
Scope(유효범위)
스코프는 자바스크립트를 포함한 모든 프로그래밍 언어의 기본적인 개념이다. 함수와 변수는 전역 또는 코드 블록, 함수 내에 선언되고, 선언되는 위치에 따라 유효한 범위를 갖는데, 이것이 스코프이다. 어디에 선언되었는지에 따라 다른 코드가 자신을 참조할 수 있는지가 결정된다.
스코프의 범위
자바스크립트에서 스코프는 전역 스코프(Global Scope)와 지역 스코프(Local Scope)로 나뉜다.
- 전역 스코프
가장 바깥쪽 스코프. 전역 스코프에 선언된 변수는 전역변수이다. 키워드 var로 선언된 전역변수는 전역 스코프에 저장되어 console.log(window.변수명)으로 접근이 가능하지만, let, const로 선언된 전역변수는 전역 스코프에 저장되지 않고 스크립트 스코프에 저장된다!
https://stackoverflow.com/questions/40685277/what-is-the-purpose-of-the-script-scope
-
지역 스코프
기존에 var 키워드를 사용할 때에는 지역 스코프가 함수 레벨 스코프만 있었지만, ECMAScript 6에서 도입된 let 키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다. 지역스코프에 선언된 변수는 지역변수이다. 지역 스코프를 빠져나오면 어떤 키워드(var, let, const)로 선언되었든 해당 지역스코프의 지역변수는 삭제된다.(이미 삭제되었기 때문에 지역 스코프 밖에서 안으로 값을 불러올 수 없는 것!)- 함수레벨 스코프(Function-level scope)
함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역변수이며, 함수 외부에서 선언한 변수는 모두 전역변수이다. - 블록레벨 스코프(Block-level scope)
모든 코드블록 (함수, if문, for문, while문, try/catch문 등)에서 선언된 변수는 코드블록 내에서만 유효하며 코드블록 외부에서는 참조할 수 없다. 즉, 코드 블록 내부에서 선언한 변수는 지역변수이다.
- 함수레벨 스코프(Function-level scope)
var
ES5까지는 변수를 선언하는 방법은 var 키워드를 사용하는 것이었다. var는 다음과 같은 특징을 갖는데, 다음의 특징들을 이유로 let과 const 사용을 권장한다.
- 블록레벨 스코프를 무시한다. (= 함수레벨 스코프만 따른다)
- 함수 외부에 생성한 변수는 모두 전역변수이다.
- for문의 변수 선언문에서 선언한 변수를 for문 코드 블록 외부에서 참조할 수 있다.
for(var i=0;i<5;i++)
{
console.log(i); //1,2,3,4,5
}
console.log(i); //5
var는 블록레벨 스코프를 무시하므로, var i=0으로 선언된 변수 i는 블록코드 내에서만 유효한 게 아니라 전역변수처럼
사용되는 것이다. 따라서 다음과 같이 코드블록 밖의 콘솔창에서 i를 출력해도 reference error를 출력하지 않는다.
- var 키워드 생략 허용한다
전역변수와 같은 이름의 변수를 함수레벨 스코프 안에 var 키워드 없이 선언할 경우, 새로운 변수를 선언하여 할당하는 것이 아니라, 전역변수 값을 재할당한다.
i = 3;
function show(){
i = 5;
console.log(i); //5
}
show();
console.log(i); //5
위의 코드 실행 순서는 다음과 같다.
1. 1번째 줄에 의해 전역객체 윈도우에 전역변수 i=3 할당
2. 6번째 줄에 의해 show()를 실행시킨다
2-1. 3번째 줄에 의해 전역변수 i=5로 재할당
2-2. 4번째 줄에 의해 콘솔창에 5 출력하고 함수 종료
3. 7번째 줄에 의해 전역객체에 저장된 i=5 출력
-
변수 중복 선언 허용한다
let과 const는 같은 이름의 변수를 재선언할 경우, Uncaught SyntaxError: Identifier '...' has already been declared 과 같은 오류를 출력한다. 변수 중복 선언이 허용되면, 기존에 선언한 값이 덮이기 때문에 코드 전체에 큰 문제를 일으킬 수 있다. -
호이스팅
자바스크립트는 var, let, const, function, class 등의 선언을 호이스팅한다. 호이스팅이란, 선언문을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성이다. 다음의 경우, 변수 d는 호이스팅되었기 때문에 1을 출력할 수 있다.
d = 1;
console.log(d); //1
var d;
console.log(e); //undefined
var e;
let과 const
앞서 설명한 변수 선언 방식인 var의 단점을 보안하기 위해 ES6 문법에 let과 const가 추가되었다.
var와 const를 비교해보면 다음과 같다.
재선언(중복선언) | 재할당 | 호이스팅 | |
---|---|---|---|
var | ⭕ | ⭕ | ⭕ |
let | ❌ | ⭕ | ⭕ |
const | ❌ | ❌ | ⭕ |
-
let과 const는 재선언 시
Uncaught SyntaxError: Identifier '변수명' has already been declared
에러가 발생한다. -
const는 재할당 시
Uncaught TypeError: Assignment to constant variable.
에러가 발생한다. -
let과 const도 호이스팅되지만, 선언과 초기화 단계 사이에 TDZ(Temporal Dead Zone)가 존재한다.
호이스팅
호이스팅 과정
-
선언(declaration phase)
변수를 실행 컨텍스트의 변수객체(Variable Object)에 등록한다. 이 변수객체는 스코프가 참조하는 대상이 된다. -
초기화(Initialization phase)
변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는undefined
로 초기화된다. -
할당(Assignment phase)
undefined
로 초기화된 변수에 실제 값을 할당한다.
var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 즉, 스코프에 변수를 등록(선언)하고 메모리에 변수를 위한 공간을 확보한 후, undefined
로 초기화한다. 따라서 변수 선언문 이전에 변수에 접근해도 스코프에 이미 변수가 존재하므로 undefined
를 반환한다. 이후 변수 할당문에 도달하면 값이 할당된다.
console.log(a); //undefined
//이 단계에서 이미 a가 호이스팅되어 전역객체에 a:undefined로 저장되어 있다
var a = 3;
console.log(b); //undefined
var b;
let 키워드로 선언된 변수는 선언과 초기화가 분리되어 진행된다. 즉, 스코프에 변수를 등록하지만, 코드블록의 선두부터 초기화가 이루어지는 지점까지 일시작 사각지대(TDZ)가 존재한다. 초기화는 변수 선언문에 도달했을때 이루어진다.
console.log(b); //Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 33;
let c = 100;
if(true){
console.log(c); //Uncaught ReferenceError: Cannot access 'c' before initialization
let c = 300;
}
첫번째 경우 b는 초기화 단계를 거치지 않았기 때문에 메모리를 확보하거나 undefined를 할당받지 못하여 참조 에러가 출력된다. 두 번째 경우는 전역변수로 선언된 c가 존재하지만, 지역변수로 선언된 또다른 c가 호이스팅되어 TDZ에 머물러있기 때문에, 100을 콘솔에 찍지 않고, ReferenceError가 발생한다.
let foo = 1;
{
console.log(foo); //Uncaught ReferenceError: Cannot access 'foo' before initialization
let foo = 2;
}
foo가 전역변수로 선언되어있기 때문에 세번째 줄에서 1을 출력할 것 같지만, 실제로는 참조 에러를 출력한다.
블록레벨 스코프 안 foo는 지역변수이다. 따라서 지역변수 foo도 해당 스코프에서 호이스팅되고, 초기화되는 지점까지 일시적 사각지대에 빠진다. (초기화는 변수 선언문에 도달했을때 이뤄진다.) 즉, 전역변수에 같은 이름이 변수가 할당되어 있더라도 블록레벨 스코프 안의 지역변수에 대해서 호이스팅이 또다시 일어나므로, 변수를 먼저 할당해주어야 한다.
const 키워드로 선언된 변수는 선언과 할당 구문을 한번에 작성해야한다. 선언만 작성할 경우, 다음과 같은 SyntaxError가 발생한다.
const a;
//Uncaught SyntaxError: Missing initializer in const declaration
익명함수는 호이스팅되지 않는다. 기명함수는 스크립트 어느 곳에서 선언되더라도 호이스팅되어 함수를 호출할 수 있다. 하지만, 변수에 할당된 익명함수는 호이스팅되지 않는다. 호이스팅이 되는 것이 많을수록 페이지가 렌더링되기 전에 브라우저에서 사용되는 메모리가 많아지므로 첫째로, 함수 내에서 사용하는 변수는 함수레벨 스코프에서 선언하고, 익명함수를 사용해 호이스팅을 막을 수 있다.
console.log(example()); //10
function example(){
const a = 10;
return a;
}
console.log(func()); //Uncaught ReferenceError: func is not defined
const func = function(){
const b = 10;
return b;
}
console.log(func()); //Uncaught ReferenceError: func is not defined
const func = () => {
const c = 10;
return c;
}
reference
Author And Source
이 문제에 관하여(전역과 지역 스코프, 호이스팅), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@kaitlin_k/71-전역과-지역스코프저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)