[programmers] TIL_DAY-13

김민기·2022년 4월 2일
0

Programmers_TIL

목록 보기
13/21
post-thumbnail

✅ Scope

변수의 범위는 어디서부터 어디까지인가?

프로그래밍 언어를 학습하다 보면 스코프는 반드시 필요한 개념이다. 우리가 선언하는 변수들을 어디에서 선언했고, 그 변수를 어디까지 사용할 수 있는지 반드시 알아야 한다.

  스코프를 크게 두 가지 분류로 나누면, 전역 스코프와 지역 스코프로 나눌 수 있다. 변수를 전역 스코프에 선언하면, 코드 어디에서든지 전역 스코프에 있는 변수를 참조할 수 있다.
변수를 지역 스코프에 선언하면, 해당 변수를 선언한 지역에서만 참조를 할 수 있다.(하위 지역까지 포함.)

// global 
var globalVar = 10;

function isGlobal() {
  console.log(globalVar);
}
isGlobal(); // 10;

// local
function isLocal() {
  var localVar = 20;
  console.log(localVar);
}
isLocal(); // 20;
console.log(localVar); // localVar is not defined

 위 코드를 보면, 전역 스코프에서 선언한 globalVar 변수는 isGlobal()함수 내부에서 접근이 가능하다. 전역 스코프에 있는 변수는 코드 어디에서든 참조가 가능하다.

 반면 localVar 변수의 경우 isLocal()함수 내부에서 선언되었기 때문에 isLocal 함수 지역 스코프를 갖는다. 따라서 외부에서 참조가 불가능하다.

  스코프의 종류는 여기서 끝이 아니다. 지역 스코프는 블록 레벨 스코프와 함수 레벨 스코프로 나뉜다. 두 스코프는 지역을 어떻게 분류하느냐에 따라 달라진다.

  블록 레벨 스코프의 경우 {...} 블록 내부를 지역으로 사용한다. 반면 함수 레벨 스코프의 경우 function( ) {...} 블록 내부를 지역으로 사용한다.

어차피 같은 { } 내부 가리키는게 아닌가?

 여기서 말하는 블록이란, 사용자가 따로 사용하는 블록 뿐만 아니라, 반복문 조건문을 위한 블록까지도 포함한다.

// block level scope & function level scope
for(let i = 0; i < 3; i++) {
  var blockVar = 10 * i;
  console.log(blockVar); // 0 10 20
  console.log(i); // 0 1 2
}
console.log(blockVar); // 20
console.log(i) // i is not undefined

 여기서 콘솔은 어떻게 출력되는가? for문 안에서 선언된 blockVarvar로 선언했기 때문에 함수 레벨 스코프를 따른다. 따라서 여기서는 전역 변수가 된다. { } 내부에 있지만 var의 특성 때문에 여기서는 전역 변수로 취급 되어 외부에서 참조가 가능하다. 반면 ifor문 내부에서 선언되었고 let블록 레벨 스코프를 따르기 때문에 for문 내부에서만 참조가 가능하다.

function outFunctionBlock() {
  var outFunctionValue = 10;
  function inFunctionBlock() {
    var inFunctionValue = 20;
    console.log(inFunctionValue);
    console.log(outFunctionValue);
  }
  inFunctionBlock();
  console.log(outFunctionValue);
  console.log(inFunctionValue);
}

outFunctionBlock();

 이 코드를 실행하면, outFunctionBlock에서 선언한 outFunctionValueoutFunctionBlock 스코프를 갖는다. 내부에서 정의된 inFunctionBlock에서 선언한 inFunctionValueinFunctionBlock 스코프를 갖는다. 따라서 inFunctionBlock 에서 실행한 console은 20, 10을 정상적으로 출력한다. 하지만 outFunctionBlock에서 실행한 console은 outFunctionValue는 정상적으로 출력하지만, inFunctionValue is not defined 에러가 발생한다.

✅ JavaScript Scope!

 자바스크립트는 다른 언어와 달리 블록 레벨 스코프가 아닌 함수 레벨 스코프를 따른다. 이로인한 단점들을 보완하기 위해서 let const를 추가 했다.

 자바스크립트 코드를 작성할 때, 다른 언어와 달리 별도의 entry point가 없다. 따라서 변수를 생각없이 사용하다보면 전역으로 사용하기 쉽다.

Lexical Scope

var x = 1;

function foo() {
  var x = 10;
  bar();
}

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

foo();
bar();

 이 코드의 실행결과를 예측했을 때, x전역 변수로 선언되어 있고 foo 함수에서 x를 10으로 변경한 다음, bar 함수를 실행한다. 따라서 bar 함수 내부에서 변경된 x를 출력할 것이다. 결과적으로 10 10 이 출력될 것이라 생각했다.

자바스크립트는 렉시컬 스코프를 따른다.

 자바스크립트는 렉시컬 스코프를 따르기 때문에 위와 같은 예측은 틀렸다. 렉시컬 스코프를 따르면 함수를 어디서 호출하는지에 따라 스코프가 결정되는 것이 아니라, 어디서 선언하였는지에 따라 스코프가 결정된다는 것이다.

 이를 바탕으로 위 코드를 다시 예측해보면, 전역 변수 x함수 foo, bar가 선언된다. foo 함수에서 x의 값을 10으로 변경한 다음 bar 함수를 실행한다. bar 함수전역에 선언되어 있고 따라서 전역 스코프를 따른다. 전역 스코프에 있는 x는 기존의 1이기 때문에 결과적으로 foo 함수를 실행하였을 때, 1을 출력하고 bar 함수를 실행시켰을 때도 이와 같은 이유로 1이 출력된다.

렉시컬 스코프를 따르는 자바스크립트를 사용할 때는 함수의 호출 위치보다 함수의 선언 위치를 잘 알고 있어야 한다.

참고 : poiemaweb.com

✅ 정리

 프로그램을 작성할 때 변수의 유효범위, 스코프에 대해서 잘 알고 있어야 프로그램의 흐름과 동작을 정확하게 예측할 수 있다. 또한 프로그래밍을 하다보면, 전역 변수가 필요한 순간들이 많이 있다. 전역 변수만을 사용해서 코드를 작성하면 어디서든 참조를 할 수 있기 때문에 프로그램 작성이 편리하다. 하지만 여러 책이나 전문가들의 말에 의하면 전역 변수는 사용하지 않는 것을 추천한다. 전역 변수를 남발하다 보면 어느 순간 코드의 흐름을 읽는 것이 불가능해지고 코드는 더러워 진다. 따라서 전역 변수의 사용을 최소화 할 수 있는 다양한 방법들을 익히고 실천해야 한다.😅

0개의 댓글