본 내용은 아래의 링크를 참조하여 작성되었습니다
(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
2. github.com/getify/You-Dont-Know-JS/tree/2nd-ed/scope-closures
'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 |