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 Scope
와 Global 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)
에 있는 변수들을 받아서 사용할 수 있다.
하지만 전역범위에서는 아래에 위치한 지역범위의 변수들은 사용할 수 없다. 높은곳(전역변수)에 있는 물이 낮은곳(지역변수)에 있는 물을 받아들일 수 없는 것과 마찬가지다.
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에서는 let
과 const
를 등장시킴으로써 이러한 혼란을 잠재웠다.
var | let | const
차이점 1 : Function Scope & Block Scope
Function Scope : var
Block Scope : let
, const
Function Scope를 따르는 var
과는 다르게 let
과 const
는 block scope의 룰을 따른다.
line 4 에서 선언된 var 변수 i
는 line 6 에서 끝나는 블록을 벗어나서도 사용 가능하다.
line 7 에서 i
를 출력하여도 문제가 발생하지 않는다.
line 10 에서 선언한 let 변수 j
는 블록을 벗어나는 순간 사용 불가능해진다. 그리하여 lline 14 에서 ‘정의되지 않았다’는 오류를 표시한다.
차이점 2 : Redeclaration
Redeclarion (O) : var
Redeclarion (X) : let
, const
같은 스코프내에서 var
는 몇번의 재선언이든 가능하지만 let
과 const
는 재선언이 불가능하다.
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
대신 let
과 const
를 사용하도록 습관화 해야 한다.