전역 스코프(Global scope) :
코드 어디에서든지 참조할 수 있다.
지역 스코프 (Local scope or Function-level scope) :
함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
먼저 간단한 예시를 보자.
var x = 0;
{
var x = 1;
console.log(x); // 1
}
console.log(x); // 1
let y = 0;
{
let y = 1;
console.log(y); // 1
}
console.log(y); // 0
자바스크립트는 함수 레벨 스코프(function-level scope)를 따른다. 함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것이다.
.
.
전역에 변수를 선언하면 이 변수는 어디서든지 참조할 수 있는 전역 스코프를 갖는 전역 변수가 된다. var 키워드로 선언한 전역 변수는 전역 객체(Global Object) window의 프로퍼티이다.
var global = 'global';
function foo() {
var local = 'local';
console.log(global);
console.log(local);
}
foo();
console.log(global);
console.log(local); // Uncaught ReferenceError: local is not defined
변수 global는 함수 영역 밖의 전역에서 선언되었다. 자바스크립트는 타 언어와는 달리 특별한 시작점(Entry point)이 없어서 위 코드와 같이 전역에 변수나 함수를 선언하기 쉽다.
하지만 자바스크립트는 다른 C-family language와는 달리 특별한 시작점이 없으며 코드가 나타나는 즉시 해석되고 실행된다. 따라서 전역에 변수를 선언하기 쉬우며 이것는 전역 변수를 남발하게 하는 문제를 야기시킨다.
.
.
함수 레벨 스코프(Function-level scope) 🎁
var x = 'global';
function foo() {
var x = 'local';
console.log(x);
}
foo(); // local
console.log(x); // global
전역변수 x와 지역변수 x가 중복 선언되었다. 전역 영역에서는 전역변수만이 참조 가능하고 함수 내 지역 영역에서는 전역과 지역 변수 모두 참조 가능하나 위 예제와 같이 변수명이 중복된 경우, 지역변수를 우선하여 참조한다.
.
.
var foo = function ( ) {
var a = 3, b = 5;
var bar = function ( ) {
var b = 7, c = 11;
// 이 시점에서 a는 3, b는 7, c는 11
a += b + c;
// 이 시점에서 a는 21, b는 7, c는 11
};
// 이 시점에서 a는 3, b는 5, c는 not defined
bar( );
// 이 시점에서 a는 21, b는 5
};
함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것을 말한다.
자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수값들을 모두 모아서 유효 범위의 최상단에 선언한다.
{}
안에서 유효즉, 함수 내에서 아래쪽에 존재하는 내용 중 필요한 값들을 끌어올리는 것이다.
스코프의 시작 지점부터 초기화 시작 지점까지의 구간
TDZ를 설명하기 위해 먼저 변수의 선언 단계를 집고 넘어가려한다.
javascript에서의 변수는 위의 사진처럼 선언, 초기화, 할당이라는 3가지 단계의 걸쳐서 생성된다.
선언 단계(Declaration phase)
: 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계를 의미한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.초기화 단계(Initialization phase)
: 실행 컨텍스트에 존재 하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계이다. 이 단계에서 할당된 메모리에는 undefined로 초기화된다.할당 단계(Assignment phase)
: 사용자가 undefined로 초기화된 메모리의 다른 값을 할당하는 단계var 키워드 변수는 변수 선언전에 선언 단계와 초기화 단계를 동시에 진행한다.
그래서 javascript는 실행 컨텍스트 변수 객체의 변수를 등록하고 메모리를 undefined로 만들어 버린다. 그렇기 때문에 변수를 선언하기 전에 호출을 해도 undefined로 호출이 되는 호이스팅이 발생하는 것이다.
let으로 선언된 변수는 var 키워드와는 다르게 선언단계와 초기화 단계가 분리되어서 진행이 된다. 그렇기 때문에 실행 컨텍스트에 변수를 등록했지만, 메모리가 할당이 되질 않아 접근할 수 없어 참조 에러(ReferenceError)가 발생하는 것 이고, 이것을 보고 우리가 호이스팅이 되질 않는다!! 라고 오해할 수 밖에 없었던 것이다.
TDZ : 스코프의 시작 지점부터 초기화 시작 지점까지의 구간 ❗❗❗
.
.
.
일반적인 함수 선언 방식
function funcDeclarations() {
return 'enchovy';
}
funcDeclarations();
자바스크립트 언어의 특징을 활용한 선언 방식
let funcExpression = function () {
return 'enchovy';
}
funcExpression();
ES6에서 추가된 화살표 함수 방식을 정의할 수도 있다.
let funcArrow = (x,y) => (x+y)
스코프와 호이스팅
함수 선언문은 var
와 같이 함수 스코프(function scope)를 가지고 let
과 const
변수는 블록 스코프(block scope)를 갖는다. 또한, 함수 선언식은 코드가 실행되기 전에 로드되지만, 함수 표현식은 interpreter가 해당 코드 줄에 도달할 때 로드된다. 함수 선언식은 호이스팅 되지만 함수 표현식은 호이스팅 되지 않으므로 정의된 범위에서 로컬 변수의 복사본을 유지할 수 있다.
/ 선언 전에 호출되도 정상 동작된다
// 1
console.log(enchovy());
function enchovy() {
return 1;
}
때문에 함수 선언식은 블록문 밖에서 호출이 가능하다.
// 로드되지 않아 error 발생
console.log(enchovy());
const enchovy = function() {
return 1;
}
함수 표현식은 선언한 변수에 할당하는지에 따라 스코프가 달라진다.