프로그래밍 언어에서 스코프(scope, 유효 범위)는 가장 기본적인 개념이다. 특히 자바스크립트에서는 스코프에 대해 이해하지 못한다면 개발 도중 예기치 못한 오류들이 남발하는 상황에 맞닥뜨릴 수 있다. 아래 코드의 실행 결과에 대해 예측해보자.
[스코프 예제 코드]
// 자바스크립트 스코프(scope)
// 전역 변수 선언
var a = 10;
// 함수선언문으로 scope 선언
function scope() {
var a = 20;
console.log('a:', a); // 1. a.value = ?
if (true) {
console.log('a:', a); // 2. a.value = ?
var a = 50;
console.log('a:', a); // 3. a.value = ?
}
var f1 = inner();
function inner() {
var a = 30;
var b = 5;
console.log('a:', a); // 4. a.value = ?
console.log('b:', b); // 5. b.value = ?
}
console.log('a:', a); // 6. a.value = ?
console.log('b:', b); // 7. b.value = ?
}
// scope 함수 호출
scope();
console.log('a:', a); // 8. a.value = ?
console.log('b:', b); // 9. b.value = ?
예제가 조금 복잡할 수도 있다. 예제를 설명하면 크게 3 부분으로 나눠서 볼 수 있다.
위 코드에서 변수명 a, b로 선언된 변수의 값이 어떻게 바뀌는지 살펴보면 된다.
결론부터 말하면 아래와 같다.
1. a : 20
2. a : 20
3. a : 50
4. a : 30
5. b : 5
6. a : 50
7. Uncaught ReferenceError: b is not defined // 참조오류로 인해 프로그램이 멈춤
참조 오류를 무시하고 끝까지 진행된다고 가정했을 때 아래 값이 추가된다.
8. a: 10
9. Uncaught ReferenceError: b is not defined
이처럼 자바스크립트는 이름이 같은 변수를 참조할 때 스코프를 통해 어떤 변숫값을 끌어와야 할지 결정한다. 즉, 스코프는 참조 대상 식별자(identifier)를 찾아내기 위한 규칙이다.
따라서 실행 문맥 순서에서 변수 f1에 inner 함수까지 할당하고 난 뒤 console에서 b를 찍으려고 했지만 b는 이미 메모리 상에서 지워진 상태이므로 Uncaught ReferenceError: b is not defined라는 참조 오류가 발생한 것이다.
또한 만약에 inner 함수에서 변수 a에 대한 선언이 없었다면 inner 함수 안에서 찍힌 console.a는 외부 함수 scope의 지역변수 a의 참조값을 읽는다. 이처럼 스코프는 정말 중요하다.
[스코프의 구분]
자바스크립트에서 스코프는 2가지로 구분된다.
[변수의 스코프]
[자바스크립트 스코프 특징]
자바스크립트는 타 언어와는 구별되는 특징이 있다. 위 코드 중 일부를 가져왔다.
function scope() {
var a = 20;
console.log('a:', a); // 1. a.value = ?
if (true) {
console.log('a:', a); // 2. a.value = ?
var a = 50;
console.log('a:', a); // 3. a.value = ?
}
console.log('a:', a); // 4. a.value = ?
...
}
코드를 보면 if 조건문 안에서 선언된 변수 a는 조건문 안에서만 유효할 것 같다. 따라서 조건문 밖에 있는 콘솔 값에는 20이 찍힐 것 같다. 그러나 if 조건문 안에서 선언된 변수 a는 조건문 밖에서도 유효하다. 왜 그럴까??
자바나 C에서는 블록(중괄호) 안에서 선언한 변수는 해당 블록 안에서만 유효하지 않는가?? 이러한 특징을 블록 레벨 스코프(block-level-scope)라고 한다. 아래 자바 코드에서 확인해보자.
public class HelloWorld {
public static void main(String[] args) {
test();
}
public static void test(){
if(true) {
int x = 10;
System.out.println("x : " + x); // x : 10
}
System.out.println("x : " + x); // 에러
}
}
//Compilation Errors Detected
//File: ALIEN_31015/source/domain/HelloWorld.java
//Line: 13
//cannot find symbol
//symbol: variable x
//location: class domain.HelloWorld
test라는 메서드를 main에서 실행시켰을 때 if 조건문 안에서 선언된 변수 x는 해당 블록 안에서는 접근 가능하지만 블록 밖에서는 변수를 찾을 수 없다는 오류가 발생한다.
그러나 자바스크립트는 기본적으로 함수 레벨 스코프(function-level-scope)를 따른다. 이는 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다는 것이다.
function scope() {
var a = 20;
console.log('a:', a); // 1. a.value = 20
if (true) {
console.log('a:', a); // 2. a.value = 20
var a = 50;
console.log('a:', a); // 3. a.value = 50
}
console.log('a:', a); // 4. a.value = 50
...
}
console.log('a:', a); // Uncaught ReferenceError: a is not defined
즉, if 조건문의 블록과 상관없이 해당 변수가 선언되어 있는 함수 코드 블록 내부이기 때문에 마지막 콘솔 실행 문맥에서도 변수 a에 대한 참조가 가능할 뿐만 아니라 조건문 내부에서 접근한 변수를 변경하더라도 그 값이 적용된다는 의미다.
그리고 해당 함수 외부 환경에선 접근이 불가능하다.
이러한 자바스크립트 특징 때문에 타 언어와 다르게 변수 선언에 대한 자유로움이 있었고 그로 인한 이점도 있었지만 전역 변수를 남발하게 되는 현상이 생겼다. 또한 동일한 변수명으로 재선언, 재할당이 가능했기 때문에 프로그램이 고도화되었을 때 메모리 누수, 디버깅의 어려움, 가독성 저하 등의 문제로 인해 유지보수가 힘들다는 문제점이 발생했다.
이러한 문제들을 해결하고자 자바스크립트에서도 ECMAScript6 이후 블록 레벨 스코프를 갖는 새로운 변수 선언 키워드인 let, const가 등장하였다. 이 내용은 다음글에서 살펴보겠다.
[JavaScript] 자바스크립트 즉시 실행 함수 (0) | 2020.08.21 |
---|---|
[JavaScript] 자바스크립트 var, let, const (2) | 2020.08.20 |
[JavaScript] 자바스크립트 프로그램의 평가와 실행 과정 (0) | 2020.08.19 |
[JavaScript] 자바스크립트의 this (0) | 2020.08.18 |
[JavaScript] 자바스크립트 배열의 기초 (0) | 2020.08.18 |