JS

# 33 가지 Javascript 필수 개념 - 6. Scope

728x90

본 내용은 아래의 링크를 참조하여 작성되었습니다

(github.com/leonardomso/33-js-concepts#6-function-scope-block-scope-and-lexical-scope)

 

 

# Scope 

scope 란 사전적 의미로는 '범위' 를 말한다

이 포스팅에서 다뤄볼 것은 '변수의 (유효) 범위' 를 다뤄 볼 것이다

 

모든 프로그래밍 언어가 그렇듯, 

변수의 범위를 다루는 것은 가장 기초적이면서도, 

꽤나 중요한 부분 중 하나이다.

 

 

* Global Scope, Local Scope

보통 변수의 범위 하면, 전역 변수(Global) 와 지역 변수(Local) 로 나뉜다

전역 범위는 코드 어디에서든 참조할 수 있는 범위고, 

지역 범위는 특정 구간에서만 참조할 수 있는 범위이다.

 

'지역' 에 대한 정의는 언어마다 다른데,

C 계열의 언어들은 Block 이 지역의 구분점이며,

JS 는 Function 이 지역의 구분점이다.

var x = 'global';
 
function foo () {
  var x = 'function scope';
  console.log(x);
}
 
foo();
console.log(x); 
cs

 

위 코드를 보면, 중복된 이름의 두 변수 x 가 존재한다.

 

첫째줄에 선언한 변수 x 는 전역 변수이며,

foo 함수 내에 선언된 변수 x 는 지역 변수 이다.

 

위 코드의 실행 결과는 

foo() 실행시 function scope 가 출력되고

console.log(x) 실행시 global 이 출력된다.

그 이유는, 먼저  foo() 를 호출할때, foo() 함수 내부의 지역 변수 x 와 지역 범위의 console.log(x) 를 수행하기에

function scope 가 출력되고, 다음 줄의 console.log(x) 는 foo 내부의 지역 변수 x 를 참조할 수 없으므로, 전역 변수 x 를 참조해 global 을 출력하기 때문이다.

 

(참고로, JS 에 선언된 전역 변수는 Global Object 에 담기게 되며, 클라이언트 부분에선 Global Object 가 window 객체이며, 서버 부분에선 global 객체를 의미한다)

 

 

* JS 초기의 변수 선언 (Function Scope)

JS 초창기에는 변수 선언시에 "var" 라는 키워드를 사용해서 선언했었다

var name = "javascript";
cs

 

var 키워드를 사용한 변수의 범위는 기본적으로 function 단위이다.

다음의 두 예제를 보면, function 단위의 범위 설정이 갖는 문제점을 볼 수 있다

 

(1) 함수내부에 선언한 var 변수

function myFunc() {  
  var name = 'kim'
  console.log(name); // 'kim'
}
 
myFunc();
 
console.log(name); // name is not defined  
cs

 

 

(2) 조건문 내부에 선언한 var 변수

if(true) {  
  var name = 'kim'
}
 
console.log(name); // 'kim'  
cs

 

JS 가 아닌 다른 언어(C++, JAVA 등) 를 하다가 이 코드들을 보면 의문점이 생긴다

C++ 이나 java 같은 언어들은 (2) 번 경우도 (1) 번 처럼 name 이 정의가 되지 않은(undefined) 변수가 되는데

JS 는 정의가 된 변수로 취급하기 때문이다.

그리고 JS 외에 다른 언어를 모르더라도 아래의 예제를 보면 그 차이를 알 수 있다.

 

- 함수

var name = 'kim';
 
const func = () => {  
  var name = 'park';
  console.log(name); // 'park'
}
 
func();
 
console.log(name); // 'kim'  
cs

 

- 조건문

var name = 'kim';
 
if (true) {  
  var name = 'park';
  console.log(name); // 'park'
}
 
console.log(name); // 'park'  
cs

 

만약 여러개의 함수와 함수 내에 여러개의 조건문을 쓰면서 

점점 코드의 복잡도가 늘어나게 되면, 상당히 헷갈리게 될 것임을 예상할 수 있기 때문이다.

 

 

 

 

* let, const (Block Scope)

ES6 가 들어서면서, 이런 단점을 보완하기 위해 let 과 const 가 등장하였다.

let 과 const 의 scope 는 함수 단위가 아닌 block 단위 이다.

 

- 함수

let name = 'kim';
 
const func = () => {  
  let name = 'park';
  console.log(name); // 'park'
}
 
func();
 
console.log(name); // 'kim'  
cs

 

- 조건문

let name = 'kim';
 
if (true) {  
  let name = 'park';
  console.log(name); // 'park'
}
 
console.log(name); // 'kim'  
cs

 

기존의 var 보다 좀 더 코드의 구분선이 명확해진것을 알 수 있다.

 

추가적으로 위의 코드에 쓰이지 않은 const 는 상수를 의미한다 

 

function scope 와 block scope 는 반복문에서도 차이가 나타난다

 

- var 를 이용한 반복문

var printsToBeExecuted = [];
 
for (var i = 0; i < 3; i++) {
    printsToBeExecuted.push(() => console.log(i));
}
 
printsToBeExecuted.forEach(f => f()); // output : 3 3 3 
cs

 

- let 을 이용한 반복문

var printsToBeExecuted = [];
 
for (let i = 0; i < 3; i++) {
    printsToBeExecuted.push(() => console.log(i));
}
 
printsToBeExecuted.forEach(f => f()); // output : 0 1 2
cs

 

위에서 알 수 있듯, var 변수로 선언한 i 의 경우,

반복문도 조건문 처럼 function scope 로 취급되지 않았기 때문에

i 의 최종 값인 3만 저장되어 함수로 나타나게 된다.

그래서, let 을 쓰면 프로그래머가 기대하던 대로 0 1 2 의 결과를 낼 수 있게 된다.

 

 

* Lexical Scope

프로그래밍 언어가 함수의 Scope 를 결정할때 두가지 방식을 사용한다

 

1. Dynamic Scope

 

2. Lexical Scope (=Static Scope)

 

 

첫번째는, 함수를 어디서 호출 했는가에 따라 상위 scope 를 결정하는 방식이며,

두번째는, 함수를 어디다가 선언했는지에 따라 상위 scope 를 결정하는 방식이다.

JS 는 두번째 방식을 따르는데, 

var x = 1;
 
function foo() {
 var x = 10;
 bar();
}
 
function bar() {
 console.log(x);
}
 
foo();
bar();
cs

위 코드에 대한 결과는 둘다 1을 출력하게 된다

어디에서 호출 했는지를 신경 쓰지 않고

어디다 선언 했는가가 중점이기 때문이다.

foo 와 bar 함수 모두 전역 범위에서 선언되었으므로,

전역 변수 var x = 1; 을 참조하여 

console.log(x) 의 결과가 1 이되기 때문이다.

 

 

 

* 전역 변수와 다른 방안

프로그램에서 전역 변수를 많이 사용하는 것은 

변수의 이름이 중복될 가능성을 높이며, 

예상치 못한 재할당 과정을 일으킬 수 있다.

이는 코드 예측을 어렵게 만들기 때문에 가급적 최소화하는것이 좋다

 

이에 대한 대안책으론

1. 전역 변수 객체를 하나 만들어 놓고 사용한다

var APP = {};
 
APP.person = {
 name'park'
};
 
console.log(APP.person.name);
cs

 

2. 즉시실행함수 IIFE(Immediately-Invoked Function Expression) 를 사용한다.

(function () {
  var APP = {};
 
  APP.person = {
    name'park',
  };
 
  console.log(APP.person.name); // park
}());
 
console.log(APP.person.name); // APP is undefined
cs

 

즉시실행함수란 한번 실행되고 전역 범위에서 사라지는 함수를 말한다

 

 

 

 

- References

1. poiemaweb.com/js-scope

2. github.com/getify/You-Dont-Know-JS/tree/2nd-ed/scope-closures

 

 

728x90

'JS' 카테고리의 다른 글

# 33 가지 Javascript 필수 개념 - 7. Expression vs Statement  (0) 2020.09.27
schema.prisma 정리  (0) 2020.09.10
prisma 2 query 문제  (0) 2020.09.09
GraphQL Server 구성  (1) 2020.09.07
webpack (1)  (0) 2020.09.06