티스토리 뷰

▶스코프(Scope)

스코프(Scope)변수나 매개변수에 접근할 수 있는 범위를 결정합니다.

자바스크립트의 스코프는 함수와 블록 단위의 스코프로 나눌 수 있으며, 함수나 블록의 선언 위치에 따라 중첩된 스코프가 정의될 수 있습니다.

이번 글에서는 스코프의 종류와 렉시컬 스코프 규칙, 스코프 체인을 통한 검색 방법에 대해 설명할 것입니다.

그리고 자바스크립트의 유명한 특징인 호이스팅의 동작 방식과 모튤 스코프를 정의하고 사용하는 법에 대해서도 알아보겠습니다.

 

1) 함수 스코프와 블록 스코프

자바스크립트에서는 함수 스코프와 블록 스코프가 존재합니다.

우선 자바스크립트에서 가장 흔하게 사용되는 함수 스코프부터 알아봅시다.

▷함수 스코프와 var

함수 스코프는 말 그대로 선언된 함수 단위로 생성되는 스코프이며, 함수 스코프 안에 선언된 변수나 함수들은 모두 함수 스코프에 포함됩니다.

function foo() {
	var a = 1;
   	function bar(b) {
   		consolo.log(a, b); // 1, 2
    }
  	bar(2);
}
foo();

foo() 함수가 선언되면서 함수 스코프를 생성하며, foo() 함수 스코프에는 변수 a와 또 다른 함수 bar()가 포함됩니다.

 

var 키워드로 선언된 변수는 [변수 선언]에서 보았듯이 함수 스코프를 따르기 때문에 블록을 무시하고 함수의 몸체 안에서 접근할 수 있습니다.

function foo() {
	if (true) {
   		var a = 1;
    }
   	console.log(a); // 1
}
foo();

변수 a를 블록 안에서 선언하였지만, var 키워드는 함수 스코프를 따르기 때문에 조건문 블록을 무시하고 함수 몸체 안에서 접근할 수 있습니다.

 

▷블록 스코프와 let, const

블록 스코프에서는 변수의 유효 범위를 블록({}) 단위로 제한하여 사용할 수 있습니다.

let과 const 키워드로 선언된 변수는 블록 스코프를 따르며, 함수 스코프의 문제를 해결할 수 있습니다.

function foo() {
	if (true) {
    		const a = 1;
    }
    console.log(a); // Uncaught ReferenceError: a is not defined
}
foo();

const 키워드를 사용해 선언한 변수 a는 블록 안에서만 유효하며, 블록을 벗어나서는 접근할 수 없습니다.

이는 훨씬 직관적이고 명료합니다.

 

var와 let, const에 대해서는 [변수 선언]에서 설명하였지만, 스코프를 알면 더욱 명확히 이해할 수 있기 때문에 한 번 더 설명을 하였습니다.

다시 이야기하지만 변수가 블록 스코프를 갖도록 선언하는 것이 직관적이면 버그를 줄일 수 있습니다.

 

정리해보면

var 키워드 : 함수 스코프. 함수 안 변수라면 접근 가능

let, const 키워드 : 블록 스코프. 블록({}) 안 변수라면 접근 가능

 

2) 렉시컬 스코프

프로그래밍 언어의 스코프는 대부분 동적 스코프와 렉시컬 스코프 두 가지 방식으로 동작합니다.

동적 스코프는 런타임 중 함수의 호출에 의해 결정되고, 렉시컬 스코프변수나 함수를 어디에 작성하였는가에 기초하여 결정됩니다.

대부분의 현대 프로그래밍 언어들은 렉시컬 스코프 규칙을 따르고 있으며, 자바스크립트 역시 렉시컬 스코프를 기반으로 동작하는 언어입니다.

 

[NOTE]

종종 자바스크립트가 동적 스코프를 따른다고 오해하는 개발자들이 있습니다.

이는 자바스크립트 this 바인딩과 스코프를 착각하기 때문입니다.

자바스크립트의 스코프는 코드가 작성된 문맥에 다라 정적으로 결정되는 렉시컬 스코프를 따릅니다.

다만, this 바인딩만 함수를 호출하는 방법에 따라 동적으로 달라질 뿐입니다.

 

function foo() {
	var a = 1;
   	function bar(b) {
    		console.log(a, b); // 1, 2
   	}
   	bar(2);
}
foo();

예제 코드르 보면 3개의 스코프가 중첩되어 있습니다.

함수의 중첩 스코프

1번 전역 스코프 안에는 foo() 함수만 존재하며 foo() 함수는 2번 함수 스코프를 생성합니다.

2번 함수 스코프에서는 변수 a와 함수 bar()를 선언하였으며, bar() 함수는 3번 함수 스코프를 생성합니다.

그리고 3번 함수 스코프 안에는 매개변수 b가 존재합니다.

 

스코프는 함수를 어디서 작성했는가에 따라 명확한 경계를 가지며, 동일한 경계를 가진 스코프는 존재할 수 없습니다.

즉 렉시컬 스코프 규칙에 따라 스코프의 경계가 결정되는 것입니다.

 

예제 코드를 실행하면 최종적으로 console.log() 메서드를 실행합니다.

이 메서드에서 참조하고 있는 변수 a를 스코프에서 어떻게 검색하는지 단계 별로 보겠습니다.

 

1단계

console.log()메서드에서 참조된 변수 a를 찾기 위해 bar() 함수의 스코프부터 검색을 시작합니다.

2단계

bar() 함수의 스코프에는 변수 a를 찾을 수 없으므로 가장 가까운 상위 스코프 foo() 함수 스코프로 올라가 검색합니다.

3단계

foo() 함수 스코프에서 변수 a를 찾아 사용하며, 검색은 여기서 종단됩니다.

 

이 규칙은 매개변수 b를 찾을 때도 동일하게 적용됩니다.

단, 매개변수 b는 bar() 함수 스코프에서 바로 찾을 수 있으므로 foo() 함수 스코프까지 올라가지 않습니다.

이렇게 중첩된 스코프 내에서 코드가 실행된 경우, 가장 안쪽의 스코프부터 시작하여 상위 스코프로 올라가며 원하는 대상을 검색합니다.

그리고 대상을 찾는 즉시 검색을 중단합니다.

여기서 중요한 점은 안쪽부터 상위로 올라가며 검색하기 때문에 상위 스코프에서 안쪽 스코프의 변수나 함수에는 접근할 수 없다는 점입니다.

function foo() {
	var a = 1;
   	function bar(b) {
   		console.log(a, b);
    }
    bar(2);
}
console.log(a); // Uncaught ReferenceError: a is not defined
foo();

이러한 스코프들의 연결 관계를 스코프 체인이라고 하며, 스코프 체인을 따라 검색하는 과정을 스코프 체이닝이라고 합니다.

 

[NOTE]

자바스크립트에서는 with문과 eval() 함수를 사용하여 렉시컬 스코프를 동적으로 변경할 수 있습니다.

하지만 두 방법 모두 스코프 체인 구조를 동적으로 변경하기 때문에 성능을 저하시키며 보안상으로도 문제가 발생할 수 있습니다.

두 방법 모두 안티 패턴으로 분류되고 있으니 사용하지 않는 것을 권장 드립니다.

 

함수 스코프와 블록 스코프를 렉시컬 스코프와 헷갈리면 안됩니다.

함수 스코프와 블록 스코프는 스코프 단위이며, 렉시컬 스코프는 이 스코프들의 범위를 결정하는 규칙입니다.

 


 

여기까지 자바스크립트 스코프에 대해 알아보았습니다 :)

728x90
LIST
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함