All Articles

Scoping and Variables

scope.png

Global Variable | Local Variable

함수 안에서 선언된 변수는 지역변수가 된다.
함수 안에서 선언되지 않은 변수는 전역변수이다.
이는 JavaScript가 function scope의 룰을 따르고 있기 때문이다.

JavaScript에서는 변수를 선언하지 않고 초기화하여도 오류가 발생하지 않는다. 자바스크립트 엔진이 이러한 변수를 전역변수로 만들어 주기 때문이다.

JavaScript에서 최상단에 있는 객체는 window이다. 어떠한 웹브라우져이든 최상단 객체로 window객체를 갖는다. 이는 생성되는 모든 전역변수들이 window의 멤버라는 것을 의미한다. (단, Node.js에서만큼은 예외이다.)

Global Scope | Local Scope

프로그래밍 언어는 저마다의 특징을 가지고 있는데 자바스크립트의 scope는 특히 그러하다.
C, C++, Java 와 같은 언어는 block scope를 사용하지만 JavaScript, Python같은 경우는 function scope를 사용한다. block scope와 function scope의 차이를 잘 숙지하고 있어야 예상치 못한 결과가 나오지 않도록 방지할 수 있다.

범위라는 의미를 갖고 있는 Scope는 변수의 유효범위를 말한다. JavaScript에서 스코프는 Local ScopeGlobal Scope로 나뉜다.

Global Scope는 최상단에 위치해 있기에 어디서나 접근가능하다. 스코프가 여러겹 중첩되어 있을시 하단에 있는 스코프라면 얼마나 여러번 중첩되어 있던지 간에 전역 스코프에 접근이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Global scope <-- Local Scope (O)
 
var monday = "Energetic";
 
function wednesday(){
  var wedShout = "seems alright";
  
  function friday(){
    var friShout = "Thanks God It's Friday";
    
    console.log(monday + ' ' + wedShout + ' ' + friShout);
  }
  friday();
}
 
wednesday(); // "Energetic seems alright Thanks God It's Friday"
cs

중첩스코프를 폭포수로 비유해보자. 자연의 물줄기는 위에서(상단) 아래로(하단) 떨어진다. 아래에 있는 물은 자신의 위치보다 높은곳에 있는 물을 받아들인다. 반대의 현상은 일어날 수 없다. 이처럼 함수안에 선언되어 전역범위보다 하단에 있는 지역범위(local scope)에서는 전역범위(global scope)에 있는 변수들을 받아서 사용할 수 있다.

waterfalls.png

하지만 전역범위에서는 아래에 위치한 지역범위의 변수들은 사용할 수 없다. 높은곳(전역변수)에 있는 물이 낮은곳(지역변수)에 있는 물을 받아들일 수 없는 것과 마찬가지다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Global scope --> Local Scope (X)
 
var monday = "Energetic";
 
function wednesday(){
  var wedShout = "seems alright";
  
  function friday(){
    var friShout = "Thanks God It's Friday";
    
  }
  friday();
}
 
console.log(monday + ' ' + wedShout + ' ' + friShout);
// ReferenceError
cs

global scope에서 local scope에 있는 변수들을 끌어다 쓰려고 할시에는 오류가 발생한다.

Function scope | Block scope

Block Scope는 블록마다{ } 범위가 정해지는 반면 Function scope는 함수에 의해서만 범위가 정해진다.

Block Scope의 룰을 따르고 있는 C언어를 보자.
Block안에서 선언한 변수 i는 해당 for의 { }블록을 벗어나면 사용할 수가 없다. 함수의 블록이든 for, if의 블록이든지 간에 { }을 기준으로 변수의 유효범위가 정해지기 때문이다. 블록의 시작{부터 블록의 끝}까지가 유효범위가 된다.

1
2
3
4
5
6
7
8
9
// C | Block Scope
#include <stdio.h>
 
int main(){
    for(int i = 0; i < 3; i++){
        printf("i : %d\n",i); 
    }
    printf("Final i: %d\n", i); //error : Use of undeclared identifier 'i'
}
cs

반면 function scope의 룰을 따르는 JavaScript는 변수i를 for{ }블록안에서 선언하였더라도 해당 블록 밖에서도 사용가능하다. 이는 블록에 영향을 받지 않는다는 뜻이다. 함수가 생성되야만 local scope가 생성이 된다.

1
2
3
4
5
6
// JavaScript | Function Scope
for(var i = 0; i < 3; i++){
  console.log(i); // 0, 1, 2
}
 
console.log("Final i : " + i); // "Final i : 3"
cs

ES6에서는 letconst를 등장시킴으로써 이러한 혼란을 잠재웠다.

var | let | const

차이점 1 : Function Scope & Block Scope

Function Scope : var
Block Scope : let, const

Function Scope를 따르는 var과는 다르게 letconst는 block scope의 룰을 따른다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Compare var & let
 
// Function scope : var
for(var i = 0; i < 3; i++){
  console.log(i); // 0, 1, 2
}
console.log("Final i : " + i); // "Final i : 3"
 
// Block scope: let
for(let j = 0; j < 3; j++){
  console.log(j); // 0, 1, 2
}
 
console.log("Final j: " + j); // ReferenceError: j is not defined

line 4 에서 선언된 var 변수 iline 6 에서 끝나는 블록을 벗어나서도 사용 가능하다.
line 7 에서 i 를 출력하여도 문제가 발생하지 않는다.
line 10 에서 선언한 let 변수 j는 블록을 벗어나는 순간 사용 불가능해진다. 그리하여 lline 14 에서 ‘정의되지 않았다’는 오류를 표시한다.

차이점 2 : Redeclaration

Redeclarion (O) : var
Redeclarion (X) : let, const

같은 스코프내에서 var는 몇번의 재선언이든 가능하지만 letconst는 재선언이 불가능하다.

1
2
3
4
5
6
7
// first declartion
var dog = 'bark';
let cat = 'meow';
 
// second declartion
var dog = 'social';
let cat = 'independent'// SyntaxError: redeclaration of let cat
cs

그렇담 스코프를 달리해서 let을 사용하여 보자.
character 라는 함수를 만들어서 let을 통해 cat이라는 변수를 다시 만들어주었다. 비록 cat은 두번 선언되었지만 유효범위가 global 과 local로 나누어져 있기에 문제가 발생하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Declaration
var dog = 'bark';
let cat = 'meow';
 
// Redeclaration
var dog = 'social';
console.log(dog); // social
 
function character(){
  let cat = 'independent';
  console.log(cat);
}
 
character(); // independent
console.log(dog); // social
console.log(cat); // meow
cs

line 14 : 함수 character를 호출하니 지역범위에 속해있는 cat의 값이 찍혔다.
line 15 : dog 를 출력하니 line 6에서 재정의된 값이 나왔다.
line 16 : cat 을 출력하니 line 3에서 정의한 전역범위의 cat 값이 출력되었다. line 10 에 있는 cat변수는 character라는 함수 내에서만 유효성을 갖기 때문이다.

이처럼 예상치 못한 오류를 줄일 수 있기에 var 대신 letconst를 사용하도록 습관화 해야 한다.

References