Scope (유효범위)
호이스팅에 대해 알기 위해선 먼저 전역과 지역 스코프가 무엇인지 알아야 된다.
- 함수와 변수는 전역 또는 코드 블록, 함수 내에 선언되며, 선언된 위치에 따라 유효범위를 갖는다
어디에 선언되었는지에 따라 다른 코드가 자신을 참조할 수 있는지 결정됨
스코프의 범위
자바스크립트에서 스코프는 전역 스코프(Global)와 지역 스코프(Local Scope)로 나뉜다.
1. 전역 스코프
- 가장 바깥쪽 스코프
전역 스코프에 선언된 변수는 전역 변수 - var 키워드로 선언된 전역 변수는 전역 스코프에 저장되어 console.log(window.변수명)으로 접근 가능
- let, const 키워드로 선언된 전역 변수는 전역 스코프에 저장되지 않고 스크립트 스코프에 저장된다
var a = 1
let b = 2
const c = 3
//위의 키워드들의 스코프를 보면 아래와 같다
Script
b: 2
c: 3
Global
a: 1
2. 지역 스코프
- 지역 스코프에 선언된 변수는 지역 변수
- 지역 스코프를 빠져 나오면 어떤 키워드(var, let, const)로 선언되었든 해당 지역 스코프의 지역 변수는 삭제 된다
이미 삭제 되었기 때문에 지역 스코프 밖에서 안으로 값을 불러올 수 없음
쉽게 안에선 밖을 볼 수 있고 밖에선 안을 볼 수 없는 거울유리를 생각하면 됨
3. 함수레벨 스코프
- 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다
함수 내부에서 선언한 변수는 지역 변수이며, 함수 외부에서 선언한 변수는 모두 전역 변수
4. 블록레벨 스코프
- 모든 코드 블록에서 선언된 변수는 코드블록 내에서만 유효하며, 코드 블록 외부에서는 참조할 수 없다
함수, if문, for문 while문, try/catch문... 등등 - 코드 블록 내부에서 선언한 변수는 지역변수
var 키워드 특징
ES6 이후부터는 아래와 같은 이유로 let과 const 사용을 권장한다
1. 블록레벨 스코프를 무시하며, 함수레벨 스코프만 따른다
- 블록레벨 스코프를 무시하며, 함수레벨 스코프만 따른다
- for문의 변수 선언문에서 선언한 변수를 for문 코드 블록 외부에서 참조할 수 있음
for(var i=0;i<5;i++)
{
console.log(i); //0,1,2,3,4
}
console.log(i); //5
//var는 블록레벨 스코프를 무시한다.
//var i=0으로 선언된 변수 i는 블록코드 내에서만 유효한 게 아니라 전역변수처럼 사용된다.
//따라서 다음과 같이 코드블록 밖의 콘솔창에서 i를 출력해도 reference error를 출력하지 않는다.
2. 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 출력
3. 변수 중복 선언 가능
- 변수 중복 선언이 허용되면 기존에 선언한 값이 덮어씌워지기 때문에 코드 전체에 큰 문제를 일으킬 수 있음
let과 const는 재선언이 불가능 → Uncaught SyntaxError: Identifier '...' has already been declared 오류 출력
4. 호이스팅
- 선언문을 해당 스코프의 선두로 옮긴 것 처럼 동작하는 특성이 있음
자바스크립트는 var, let, const, function class 등의 선언을 호이스팅한다
a = 1;
console.log(a); //1
var a;
//변수 a는 호이스팅되었기 때문에 1을 출력한다
console.log(b); //undefined
var b;
console.log(c); // Error: Uncaught ReferenceError: bar is not defined
let bar;
//let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조에러(ReferenceError)가 발생
let & const
- let & const 키워드는 재선언 시 Uncaught SyntaxError: Identifier '변수명' has already been declared 에러 발생
- const 키워드는 재할당 시 Uncaught TypeError: Assignment to constant variable. 에러 발생
- let & const 키워드는 호이스팅이 되지만, 선언과 초기화 단계 사이에 TDZ(Temporal Dead Zone)가 존재함
재선언 (중복선언) | 재할당 | 호이스팅 | |
var | 가능 | 가능 | 가능 |
let | 불가능 | 가능 | 가능 |
const | 불가능 | 불가능 | 가능 |
호이스팅 과정
1. 선언 declaration phase
변수를 실행 컨택스트의 변수 객체에 등록하며, 이 변수객체는 스코프가 참조하는 대상이 된다
2. 초기화 Initialization phase
변수 객체에 등록된 변수를 위한 공간을 메모리에 확보하며, 이 단계에서 변수는 undefined로 초기화 된다
3. 할당 Assignment phase
undefined로 초기화된 변수에 실제 값을 할당한다
1. var 키워드로 선언
- 선언 단계와 초기화 단계가 한번에 이루어짐
스코프를 변수에 등록(선언)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화한다
변수 선언문 이전에 변수에 접근해도 스코프에 이미 변수가 존재하므로 undefined를 반환 - 변수 할당문에 도달하면 값이 할당됨
선언단계 | 초기화 단계 | foo === undefined | |
var foo; | |||
foo = 1; | 할당 단계 | foo === 1 | |
var 키워드로 선언된 변수의 생명 주기 |
// 스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 있다.
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
2. let 키워드로 선언
- 선언과 초기화가 분리되어 진행
스코프에 변수를 등록하지만 코드블록의 선두부터 초기화가 이루어지는 지점까지 일시적 사각지대(TDZ)가 존재함 - 초기화는 변수 선언문에 도달했을때 이루어짐
선언단계 | ReferenceError | ||
let foo; | 일시적 사각지대 | ||
foo = 1; | 초기화 단계 | foo === undefined | |
할당 단계 | foo === 1 | ||
let 키워드로 선언된 변수의 생명 주기 |
// 스코프의 선두에서 선언 단계가 실행된다.
// 아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 없다.
console.log(foo); // ReferenceError: foo is not defined
let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
let foo = 1; // 전역 변수
{
console.log(foo); // ReferenceError: foo is not defined
let foo = 2; // 지역 변수
}
위 예제의 경우, 전역 변수 foo의 값이 출력될 것처럼 보인다.
하지만 ES6의 선언문도 여전히 호이스팅이 발생하기 때문에 참조 에러(ReferenceError)가 발생한다.
//ES6의 let으로 선언된 변수는 블록 레벨 스코프를 가지므로 코드 블록 내에서 선언된 변수 foo는 지역 변수이다.
//따라서 지역 변수 foo도 해당 스코프에서 호이스팅되고 코드 블록의 선두부터 초기화가 이루어지는 지점까지 일시적 사각지대(TDZ)에 빠진다.
//따라서 전역 변수 foo의 값이 출력되지 않고 참조 에러(ReferenceError)가 발생한다.
let foo = 1;
{
console.log(foo); //Uncaught ReferenceError: Cannot access 'foo' before initialization
let foo = 2;
}
//foo가 전역 변수로 선언되어있기 때문에 세번째 줄에서 1을 출력할 것 같지만 참조 에러를 출력한다.
//블록레벨 스코프안 foo는 지역변수이므로 지역변수 foo도 해당 스코프에서 호이스팅 되며,
//초기화 되는 시점까지 일시적 사각지대에 빠진다 → 초기화는 변수 선언문에 도달했을때 이루어진다.
//즉, 전역변수에 같은 이름이 변수로 할당되어 있더라도 블록레벨 스코프 안의 지역변수에 대해서
//호이스팅이 또 다시 일어나므로, 변수를 먼저 할당해주어야 한다.
3. const 키워드로 선언
- 선언과 할당 구문을 한번에 작성해야함
const a;
//Uncaught SyntaxError: Missing initializer in const declaration
//선언만 할 경우 위와 같은 SyntaxError 에러 발생
4. 익명함수
- 익명함수는 호이스팅 되지 않는다
- 기명함수는 스크립트 어느 곳에 선언되더라도 호이스팅되어 함수를 호출할 수 있다
- 호이스팅 되는 것이 많을 수록 페이지가 렌더링 되기 전에 브라우저에서 사용하는 메모리가 많아진다
함수 내에서 사용하는 변수는 함수레벨 스코프에서 선언하고, 익명함수를 사용해 호이스팅을 막을 수 있다
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
let, const와 블록 레벨 스코프
반응형
'CodeStates > JavaScript' 카테고리의 다른 글
[JavaScript] 고차함수 (0) | 2022.02.27 |
---|---|
[JavaScript] 원시 자료형과 참조 자료형 (0) | 2022.02.09 |
[JavaScript] 배열과 객체 (0) | 2022.01.04 |
[JavaScript] 반복문 (0) | 2021.12.19 |
[JavaScript] 문자열 (0) | 2021.12.19 |
댓글