Scope는 자바스크립트에서 변수를 관리할 때 쓰이는 중요한 개념이다. 자바스크립트로 코드를 짤 때, 변수의 scope를 이해하는 것은 필수적이다. 이 글에서는 이러한 자바스크립트 scope에 대한 개념을 설명하고, 이해하고자 한다.
Scope?
스코프는, 쉽게 말해 변수가 허용되는 범위를 말한다. 코드를 통해 살펴보자.
const message = 'Hello';
console.log(message); // 'Hello'
message라는 변수를 선언하고 로그를 찍어보면 당연하게 'Hello'가 출력이 되는 것을 볼 수 있다. 그렇다면 message 선언부를 if문 안에 넣고 로그를 찍으면 어떻게 될까?
if (true) {
const message = 'Hello';
}
console.log(message); // ReferenceError: message is not defined
위처럼 자바스크립트는 message라는 변수가 정의되어 있지 않다고 에러를 출력한다. 왜 이렇게 되는 것일까? 이유는, if라는 하나의 block이 message변수에 scope를 만든 것이다. 이렇게 되면 message 변수는 오직 생성된 scope내에서만 접근이 가능하다.
즉, 변수에 대한 접근은 scope에 따라 결정이 된다. 해당되는 scope내에서는 변수 접근이 자유롭지만, scope밖에서는 접근이 불가능하다.
Block scope
자바스크립트에서 let과 const를 사용하여 변수를 선언하면 block scope로 정의된다.
if (true) {
// "if" block scope
const message = 'Hello';
console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError
첫 번째 console.log는 message가 정의된 if scope 내에 있기 때문에 'Hello'를 출력하고, 두 번째 console.log는 scope범위 밖에 있기 때문에 에러를 출력한다. if뿐만 아니라 for, while문 또한 scope를 생성한다.
for (const color of ['green', 'red', 'blue']) {
// "for" block scope
const message = 'Hi';
console.log(color); // 'green', 'red', 'blue'
console.log(message); // 'Hi', 'Hi', 'Hi'
}
console.log(color); // throws ReferenceError
console.log(message); // throws ReferenceError
color와 message는 for문 내에서 선언되었으므로 for 스코프 내에 있다.
while (/* condition */) {
// "while" block scope
const message = 'Hi';
console.log(message); // 'Hi'
}
console.log(message); // => throws ReferenceError
자바스크립트에서는 그냥 중괄호로 된 코드 블럭을 선언할 수 있다. 이렇게 선언된 코드 블럭 또한 스코프를 생성한다.
{
// block scope
const message = 'Hello';
console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError
위의 예시들을 통해 let과 const가 block scope라는 것을 알았다. 그렇다면 var의 경우는 어떨까?
if (true) {
// "if" block scope
var count = 0;
console.log(count); // 0
}
console.log(count); // 0
count는 if block scope에서 선언되었다. 따라서 첫 번째 console.log에서 count가 출력이 되는 것은 당연한 것 같다. 하지만 결과를 보면 scope밖에서 count를 로그에 찍었을 때에도 출력이 되었다. 그 이유는 var가 let과 const와는 달리 Function scope이기 때문이다.
Function scope
자바스크립트에서 함수는 var, let, const로 선언된 변수에 scope를 생성한다.
function run() {
// "run" function scope
var message = 'Run, Forrest, Run!';
console.log(message); // 'Run, Forrest, Run!'
}
run();
console.log(message); // throws ReferenceError
message는 run이 만든 function scope에 들어가 있다. 따라서 두 번째 console.log처럼 함수 밖에서의 접근은 불가능하다. 똑같이, let과 const로 선언한 변수도 scope를 생성한다. 그리고 심지어 선언된 function또한 해당된다.
function run() {
// "run" function scope
const two = 2;
let count = 0;
function run2() {}
console.log(two); // 2
console.log(count); // 0
console.log(run2); // function
}
run();
console.log(two); // throws ReferenceError
console.log(count); // throws ReferenceError
console.log(run2); // throws ReferenceError
Nested scope
Scope들은 서로 중첩될 수 있다. 아래 예시에서는 run() 함수가 scope를 생성하고, 그 안에서 if가 또 다른 scope를 생성한다.
function run() {
// "run" function scope
const message = 'Run, Forrest, Run!';
if (true) {
// "if" code block scope
const friend = 'Bubba';
console.log(message); // 'Run, Forrest, Run!'
}
console.log(friend); // throws ReferenceError
}
run();
위와 같이 if스코프는 run()스코프내에 중첩되어 있다. scope가 다른 scope를 포함하고 있으면 포함된 scope는 inner scope, 포함하고 있는 스코프를 outer scope라고 한다. 위 코드의 경우 if가 inner scope, run()이 outer scope이다.
그렇다면 중첩된 scope에서 변수 접근은 어떻게 될까? 위 사진과 같이 inner scope는 outer scope의 변수들에 접근이 가능하다는 것을 기억하면 된다.
Global scope
Global scope는 가장 바깥에 있는 스코프이다. Global scope의 변수들은 어떠한 inner scope들도(Local scope) 접근이 가능하다.
<script src="myScript.js"></script>
// myScript.js
// "global" scope
let counter = 1;
위의 counter는 해당 웹 페이지에 작성된 어떠한 곳에 위치한 자바스크립트에서 접근이 가능하다. Global scope는 자바스크립트의 호스트(browser, Node)가 특정 함수나 기능들을 global variables로서 제공할 수 있게 해준다. 예를 들면, browser에서 window와 document가 global variables로 제공된다. 또한 Node에서 global variables인 process객체에 접근할 수 있다.
Lexical scope
function outerFunc() {
// the outer scope
let outerVar = 'I am from outside!';
function innerFunc() {
// the inner scope
console.log(outerVar); // 'I am from outside!'
}
return innerFunc;
}
const inner = outerFunc();
inner();
위의 코드에서 마지막 줄인 inner()를 보자. 자바스크립트는 innerFunc()내에서의 outerVar가 outerFunc()의 outerVar인 것을 어떻게 알까? Lexical scope때문이다. Lexical scope는 변수의 접근성을 중첩된 function scope의 정적인 위치에 따라 결정하겠다는 의미이다. 즉, inner function scope에서 outer function scope의 변수에 접근할 수 있다.
참고
Dmitri Pavlutin의 'JavaScript Scope Explained in Simple Words'
'JavaScript' 카테고리의 다른 글
[JavaScript] 'this'의 이해 (0) | 2020.08.07 |
---|---|
[JavaScript] 클로저(Closure)란? (0) | 2020.07.30 |
[JavaScript] 자바스크립트 동작 원리: 엔진, 런타임, 호출 스택 (0) | 2020.07.24 |
[JavaScript] Day 1 자바스크립트로 악기(드럼) 만들기 (0) | 2020.05.19 |
[JavaScript] JavaScript30 / 30일 자바스크립트 챌린지 (0) | 2020.05.19 |