[JS] Scope/ 지역 변수/ 전역 변수

아임 레조·2020년 9월 5일
2

STUDY

목록 보기
10/34
post-thumbnail
  • JavaScript의 Scope의 의미와 적용 범위를 이해할 수 있다
  • JavaScript의 Scope 주요 규칙을 이해할 수 있다
  • 중첩 규칙
  • block level vs. function level
  • let, const, var의 차이
  • 전역 변수와 전역 객체의 의미

Scope

범위, 영역을 뜻하는 말로 변수 접근 규칙에 따른 유효 범위를 말한다. 변수는 어떠한 환경 내에서만 사용이 가능하며, 프로그래밍 언어는 각각의 변수 접근 규칙을 갖고 있다. 자바스크립트는 기본적으로 함수가 선언되는 동시에(lexical) 자신만의 Scope를 갖는다. 우리는 이 범위라는 개념을 이용해서 한줄의 코드를 작성할때 어디까지가 잘 되는 코드이고 어디까지가 잘 작동되지 않는 코드인지 파악할 수 있다. 조금 더 쉽게 말해서, 앞에서는 변수를 만들고 값을 할당하면 어디서든 그 변수 이름으로 할당된 값을 가져올 수 있었다. 그런데 사실 변수에는 유효한 범위가 있어서 그 범위를 벗어나면 오류가 발생하게 된다. 변수의 유효범위에 대해서 살펴보자.

let x = 3; 
console.log(x); // 3  

이 경우 잘 실행된다. 이번에는 같은 내용을 함수 안에 넣어주겠다.

function myFunction(){
 let x = 3; 
 console.log(x);
} 

myFunction(); // 3 

이 함수도 잘 실행된다. 그렇다면 함수 밖에서 변수를 출력하게 되면 어떻게 될까?


블록문, 로컬 변수, 지역 변수(Local variable)

1 function myFunction(){ //블록문(block statement) 
2 let x = 3; // 로컬 변수, 지역 변수(Local Variable) 
3 console.log(x);
4 } 
5
6 myFunction(); // 3 
7 console.log(x); // ReferenceError: x is not defined 

어떻게 된 일일까?
자바스크립트에서는 중괄호'{}'로 감싼 코드를 블록문(Block Statement)라고 부른다. 그리고 그 블록문 안에 선언된 변수를 로컬 변수 혹은 지역 변수(Local variable)라고 부른다. 로컬변수는 블록문 내에서만 사용할 수 있는 변수이다. 변수 x의 scope 즉, 변수x가 유효한 범위는 myFunction안에서 만이다.
7번줄에서 사용한 x는 변수x가 유효한 범위 내에 있지 않기 때문에 정의되지 않았다는 오류가 발생한다.


글로벌 변수, 전역 변수(Global Variable)

이번에는 변수x를 함수 밖으로 꺼내보자.

1 let x = 3; // 글로벌 변수, 전역 변수(Global Variable)  
2 function myFunction(){  
3 console.log(x);
4 } 
5
6 myFunction(); // 3 
7 console.log(x); // 3 

이 경우 7번줄도 3을 출력함을 확인할 수 있다. 3번줄의 변수x는 1번줄에서 선언한 변수x이다. 로컬 변수와 반대로 블록문 밖에서 선언한 변수는 블록문 안에서도 사용할 수 있는데 이런 변수를 글로벌 변수 혹은 전역 변수라고 부른다. 글로벌 변수는 이름에서도 알수 있듯이 코드를 작성할 수 있는 파일 어디서나 사용할 수 있다.

그런데 만약 myFunction안에서 똑같은 이름의 변수를 다시 선언하면 어떻게 될까?

1 let x = 3; // 글로벌 변수, 전역 변수(global variable) 
2 function myFunction(){ 
3 let x = 5;  //로컬 변수, 지역 변수(local variable) 
4 console.log(x);
5 } // 2 - 5번까지 local scope 
6
7 myFunction(); // 5 
8 console.log(x); // 3 

함수의 실행 순서를 확인해보면, 1번줄부터 변수x가 선언되고 2번부터 죽 내려오다가 7번줄에서 함수 myFunction을 호출한다. 그럼 2번줄로 올라가서 myFunction함수를 실행하게 되고 3번줄에서 로컬 변수 x를 선언하게 된다. 자연스럽게 4번줄에서 사용한 변수x는 3번줄의 로컬 변수x를 사용하게 되는 것이므로 할당값인 5를 콘솔에서 출력하게 된다. 즉, 블록문 내부에서 선언된 지역 변수가 있는지를 먼저 확인하고 있으면 그 지역 변수를 사용하고 없을 경우 전역 변수를 사용한다. 5를 출력하고 함수는 종료되고 마지막 8번줄의 변수x는 블록문 밖에 있으므로 로컬 변수의 영역에서 벗어나있다. 그렇기 때문에 자연스럽게 1번 줄에서 선언한 글로벌 변수x가 사용되면서 콘솔에 3이 출력된다.


Local Scope vs Global Scope

  1. 모든 변수에는 scope 즉 변수의 유효 범위가 있는데 크게 로컬 변수와 글로벌 변수로 나눌 수 있고, 이를 나누는 기준은 '{}' 블록문이다.
  2. 로컬 변수는 블록 내에서만 유효한 범위를 가지고 있고(Local scope 안쪽에서 선언된 변수는 밖에서 사용할 수 없다) 글로벌 변수는 어디에서나 유효한 범위를 가지고 있다.
  3. 블록문 내에서 변수를 사용하려고 하면 로컬 변수를 먼저 찾아서 사용하고 없을 경우는 글로벌 변수를 사용한다. 글로벌 변수도 없다면 당연히 오류가 발생하게 된다.
  4. 안쪽 scope에서 바깥 변수나 함수에는 접근이 가능하지만 바깥쪽 scope에서 안쪽 변수나 함수에는 접근할 수 없다.
  5. Scope는 중첩할 수 있다.(즉, 함수 안에 함수 넣기가 가능하다)
  6. 지역변수는 함수 내에서 전역 변수보다 더 높은 우선순위를 가진다.

실전 문제 01

다음 코드를 실행했을 때 출력되는 내용은?

1  function myFunction() {
2    let x = "해리포터";
3    x = "호그와트";
4  }
5 
6  myFunction();
7  console.log(x);

함수 정의 후 6번줄에서 myFunction을 호출하는데 2번줄에서 문자열 "해리포터"가 로컬 변수x에 할당되고, 바로 3번줄에서 문자열 "호그와트"가 로컬 변수x에 할당된다. 그리고 함수는 끝나서 다시 함수를 호출한 6번줄로 돌아가는데, 더 이상 동작할 부분이 없으니 7번줄로 넘어간다. 7번 줄에서 x를 출력하려고 하는데, 앞서 정의한 x들은 로컬 변수이므로 함수 밖에서 언제든 접근 가능한 글로벌 변수x는 당연히 존재하지 않는다. 따라서 이 프로그램을 실행하면 오류가 발생한다.


실전 문제 02

다음 코드를 실행했을 때 출력되는 내용은?

1  let x = 100;
2  
3  function myFunction() {
4    let y = 40;
5    console.log(x + y);
6  }
7     
8  myFunction();
9  console.log(x);

8번줄에서 myFunction을 호출한다. 3번줄로 올라가서 함수가 실행되고 지역 변수y에 40을 할당하고 5번 줄에서는 console.log(x + y)를 한다. 그런데 로컬 변수x는 존재하지 않기 때문에 글로벌 변수x에 담긴 100을 가져온다. 그리고 로컬 변수y는 존재하기 때문에 로컬 변수y에 담긴 40을 사용한다. 따라서 140을 출력한다.
이후 함수는 종료되고 9번줄의 console.log(x)를 하는데, 이때는 글로벌 변수x를 사용하기 때문에 100이 출력된다. 결론적으로 콘솔에 출력된 것을 보면 아래와 같다.

140
100

Var 키워드 vs let 키워드

  • 변수를 정의하는 또다른 키워드 var
  • 자바스크립트는 기본적으로, 함수 단위로 자신만의 scope를 가진다. (var 키워드: old way) 즉, var로 변수를 선언하면 block은 없다고 생각하고 코드가 동작되며 if나 for 안에서 {} 이용해 뭔가를 선언하더라도 순서대로 코드를 실행한다.
  • 그러나 Block 단위로 Scope를 구분했을 때에 예측하기 쉬운 코드를 작성할 수 있다. (let 키워드)
  • var는 되도록이면 사용하지 않도록 한다. (Block Scope의 경우 var로 선언하면 전역변수로 바뀌게 된다)
  • let은 한번 할당된 변수값을 재이용해서 다른 값으로 할당할 일이 생겼을 때 사용하기 좋다.
  • const는 코딩을 하다가 내가 이미 할당한 값을 보호하고 싶을 때 사용한다.
// let의 경우 
// 사례1 
for(let i = 0; i<5; i++){
	console.log(i);
 } 
/*
0
1
2
3
4 */ 
console.log('final i:', i); 
// ReferenceError! 'i is not difined' 
// Block 범위를 벗어나는 즉시 변수 사용 불가능

//사례2 
let b = 10; //b는 10 선언 
if(true){
  let b = 20; 
  console.log(b);// 20 
}
console.log(b); //10 


// var의 경우
// 사례1 
for(var i = 0; i<5; i++){
	console.log(i);
 } 
/*
0
1
2
3
4 */ 
console.log('final i:', i); // 'final i: 5' 
		           // block 범위를 벗어나도 같은 function scope에서는 사용 가능 

// 사례2 
var a = 10; 
if(true){
  var a = 20; // a = 10 선언했지만 if 안에서 재선언 가능 
  console.log(a); // 20 (위의 재선언 영향) 
} 
console.log(a); // 20 (함수 안의 변수가 밖에도 영향을 미침 

const 키워드

값이 변하지 않는 변수 즉 상수(constant)를 정의할 때 사용한다. let과 동일하게 Block Scope를 따른다. 값을 재정의하려고 하면 TypeError가 난다.

const pi=3.14 
      pi = 3.1415 //uncaught TypeError 
letconstvar
유효 범위Block ScopeBlock Scopefunction Scope
값 재정의가능불가능가능
재선언불가능불가능가능

전역 변수와 Window 객체

전역 범위를 대표하는 객체 Window
Global Scope에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결된다. 단, 전역 범위에 너무 많은 변수를 선언하지 않도록 주의하자!

var myName = 'Minion'; 
console.log(window.myName); //Minion 
function foo(){
  console.log('bar');
}
console.log(foo === window.foo); //true

선언 없이 초기화된 전역변수의 위험성

절대로 선언 키워드(var, let, const)없이 변수를 초기화하지 말아라!

function showAge(){
  //age는 선언을 하지 않고 할당만 함, 
  // 에러가 나지 않고 전역 변수로 취급된다 age === window.age 
  age = 90; 
  console.log(age); 
}
showAge(); //90
console.log(age); //90 

이런 실수를 방지하고 싶을 경우, 'Strict Mode'를 사용하자. 'use strict';를 미리 적용하고 코딩 작업을 하자. 콘솔창에서는 사용할 수 없고, 파일을 저장한 다음에 사용할 수 있다.

profile
어쩌다보니 백엔드 개발자/ 번아웃 없이 재밌게 개발하고 싶어요

1개의 댓글

comment-user-thumbnail
2022년 3월 23일

혼자 책보면서 독학하다가 지역변수, 전역변수가 이해가 안되서 찾다가 보게됬는데 정리가 너무 잘되어있어서 이해하는데 큰 도움이 됬네요! 잘보고가요 감사합니닷!!

답글 달기