2019. 06. 01 수정


1. Scope란?

프로그래밍 언어에서 변수는 참조할 수 있는 범위가 존재합니다.

그래서 변수는 선언된 위치에 따라 유효범위를 갖게 됩니다.


C와 Java같은 경우는 Block Level Scope를 갖습니다.

Block이란 { ... } 을 의미하며 이 블록을 벗어나면 블록 내부에 선언된 변수를 참조할 수 없습니다.

private void foo(){
int x = 10;
}

System.out.println(x);

예를 들어, 위와 같은 Java 코드는 에러가 발생합니다.

foo() 함수 내부에 존재하는 변수 x를 외부에서 참조할 수 없기 때문이죠.


그러나 JS의 경우에는 다릅니다.

JS에서 변수를 선언하는 방법에는 var, let, const 키워드가 있습니다.

지금부터 변수의 유효범위 개념과 변수 선언 방법에 따른 scope가 어떻게 바뀌는지 살펴보도록 하겠습니다.





2. var - 함수 레벨 범위 ( Function Level Scope )

var 키워드를 사용하여 변수를 선언하면, 그 변수는 함수 레벨 범위를 갖습니다.


function foo(){
if(true){
var x = 10;
}
console.log(x);
}

foo();
console.log(x);

변수 x가 Block Level Scope라면, if 문이 끝날 때 변수 x에 대한 참조도 사라지게 됩니다.

따라서 foo() 함수 내부에 있는 console.log(x)는 출력 될 수 없을 것입니다.


그러나 var 키워드는 변수를 Function Level Scope로 정의하기 때문에, if문이 끝나도 함수 내에서 x가 유효하게 됩니다.

그런데 foo() 함수를 벗어나면 x에 대한 참조를 할 수 없기 때문에, foo() 함수 외부에 있는 console.log(x)는 에러가 발생합니다.


if(true){
var x = 10;
}

console.log(x);

이 코드는 10을 출력할까요? 에러를 발생할까요?

x는 if 내부에 선언되었지만, if는 함수가 아니기 때문에 xGlobal Scope 입니다.

따라서 정상적으로 10을 출력하게 됩니다.





3. let, const - 블록 레벨 스코프 ( Block Level Scope )

let, const 키워드를 사용하여 변수를 선언하면, 그 변수는 Block Level Scope를 갖습니다.

두 키워드의 차이점은 다음과 같습니다.

  • let으로 선언된 변수는 값을 수정할 수 있습니다.
  • const로 선언된 변수는 값을 수정할 수 없는 상수입니다.
// let
if(true){
let x = 10;
}
console.log(x);


// const
if(true){
const x = 10;
}
console.log(x);

위의 두 결과는 오류를 발생합니다.

let, const로 선언한 변수는 Block Level Scope이기 때문에 if문을 벗어나면 x에 대해 참조할 수 없습니다.





4. let과 const의 특징

다음으로 예제들을 살펴보면서 let과 const의 특징들을 살펴보도록 하겠습니다.


1) const 값 변경

const는 상수라고 했으므로 값을 변경할 수 없을텐데, 실제로 그런지 테스트 해보도록 하겠습니다.

const x = 10;
x = 20;
console.log(20);


상수로 선언한 변수에 할당하려 했다는 오류가 발생합니다.

즉, const로 선언된 변수는 값을 변경할 수 없는 것을 확인했습니다.




2) const 초기화

const로 선언된 변수는 선언하자마자 초기화해야 합니다.

const x;
x = 10;

console.log(x);


const로 선언한 변수는 초기화 해야 한다는 오류가 발생합니다.




3) let 중복 선언

let은 var와 달리 중복 선언이 되지 않습니다.

var x = 10;
var x = 20;
console.log(x);

var는 위와 같이, 같은 변수에 대해 중복 선언이 가능합니다.

결과는 20이 출력됩니다.


그러나 let은 어떨까요?

let x = 10;
let x = 20;
console.log(x);


x는 이미 선언된 변수라는 오류를 발생합니다.




4) 클로저로 인한 이슈 - let으로 해결

반복문을 작성할 때 공유변수를 let을 사용하면, 클로저에서 편리하게 다룰 수 있습니다.

( 클로저에 대한 개념은 여기를 참고해주세요 ! )

function count() {
for (var i = 1; i < 10; i++) {
setTimeout(function(){
console.log(i);
}, 1000);
}
}

count();

위 코드의 결과로 1~9까지의 값이 출력되기를 바랐지만, 실제로는 10이 9번 출력이 됩니다.


변수 i를 var가 아닌 let으로 선언했을 경우, 정상적으로 1~9까지 출력이 됩니다.

function count() {
for (let i = 1; i < 10; i++) {
setTimeout(function(){
console.log(i);
}, 1000);
}
}
count();





5. 정적 스코프 ( Static Scope )

정적 스코프는 실행되는 문맥에 의해 유효 범위가 결정되는 것을 의미합니다.


다음의 두 예제를 비교해보도록 하겠습니다.

예제1

var x = 10;

function foo(){
var x = 20;
goo();
}

function goo(){
console.log(x);
}

foo();


예제2

var x = 10;

function foo(){
var x = 20;
goo();

function goo(){
console.log(x);
}
}

foo();


두 예제의 차이는 goo() 함수가 어디에 위치했느냐 입니다.

예제1은 goo() 함수가 foo() 함수 외부에 선언되어 있고, 출력 결과는 10입니다.

예제2는 goo() 함수가 foo() 함수 내부에 선언되어 있고, 출력 결과는 20입니다.

이와 같이 함수가 선언된 시점에서 변수의 유효범위가 어떻게 결정되는지에 따라 값이 달라지는 것을 확인할 수 있습니다.

이러한 유효 범위를 갖는 scope를 Static Scope 또는 Lexical Scope라 합니다.





이상으로 JS에서의 변수 scope와 let, const 키워드에 대해 마치도록 하겠습니다.

var 키워드를 사용하는 function level scope와

let, const 키워드를 사용하는 block level scope가 있었습니다.


[ 참고 ]

http://beomy.tistory.com/7