WHAT I LEARNED/JavsScript

모던 자바스크립트 13. 스코프

보니bonnie 2024. 4. 30. 10:06
728x90

1. 스코프란?

모든 식별자(변수명, 함수명, 클래스명 등)의 유효범위 즉, 다른 코드가 식별자 자신을 참조할 수 있는 유효범위.

모든 식별자는 자신이 선언된 위치에 의해 자신이 유효한 범위가 결정된다.

let x = '반원재'; // ⓐ

function foo() {
    let x = '이규리'; // ⓑ
    console.log(x);
}

foo(); // 이규리

console.log(x); // 반원재

위의 코드처럼 동일한 변수가 다른 곳에서 선언되었을 경우, foo() 안의 x와 console.log(x)에서의 x가 무엇을 참조할 지는 자바스크립트 엔진이 결정하게 되는데, 이를 식별자 결정이라고 한다.

 

위의 코드는 코드의 가장 바깥 영역에서 선언된 x변수(ⓐ)는 어디서든 참조할 수 있다.(전역 스코프)

하지만 함수 내부에서 선언된 x변수(ⓑ)는 함수 내부에서만 참조할 수 있고, 함수 외부에서는 참조할 수 없다.(함수 스코프)

즉, 두 개의 변수는 식별자 이름은 동일하지만 스코프가 다른 별개의 변수다.

 

이렇듯 프로그래밍 언어에서는 스코프를 통해 식별자인 변수명의 충돌을 방지하여 동일한 이름의 변수를 사용할 수 있게 한다.

 

2. 스코프의 종류

스코프의 종류는 위에서 언급했듯 전역 스코프지역 스코프로 구분된다.

 

전역과 지역으로 구분되는 기준은 무엇일까?

우선 코드는 코드의 가장 바깥 영역에서 선언되는 전역(global)과 함수 몸체 내부에서 선언되는 지역(local)로 구분할 수 있다.

전역에서 변수를 선언하면 전역 변수가 되고, 이는 코드 어디서든 참조할 수 있다. 이를 전역 스코프를 갖는다고 한다.

마찬가지로 지역에서 변수를 선언하면 지역 변수가 되고 이를 지역 스코프를 갖는다고 한다.

지역 스코프에서 선언된 지역 변수는 자신이 선언된 지역 스코프와 그 하위 지역 스코프에서 유효하다. 

 

챕터 1(스코프란?)에서 설명했듯, 전역 변수와 하위 지역 스코프를 갖는 지역 변수의 변수명이 동일할 경우,

자바스크립트 엔진은 코드의 문맥을 읽어내어 스코프로 두 변수를 구분한다. 이를 스코프 체인을 통해 참조할 변수를 검색했다고 한다.

let x = '반원재'; // 전역 스코프 

// 외부 함수
function outer() {
    let y = '스코프!'; // 지역 스코프

    console.log(x); // ⓐ 반원재
    console.log(y); // 스코프!
    
    // 내부 함수
    function inner() {
        let x = '이규리'; // ⓑ 지역 스코프
        
        console.log(x); // 이규리
        console.log(y); // 스코프! 
        		// 지역변수 y는 내부 함수 inner에서 유효하다
    }
    
    inner();
}

outer();

console.log(x); // 반원재 
console.log(y); // ReferenceError: y is not defined

 

3. 스코프 체인

스코프는 전역 스코프와 지역 스코프, 그리고 지역 스코프는 외부 함수와 내부 함수로 구분될 수 있고, 이는 상위-하위 스코프로 연결된다.

스코프 체인이란, 이렇게 스코프가 계층적으로 연결된 것을 의미한다.

 

변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작해 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다. 이를 통해 상위 스코프에서 선언한 변수를 하위 스코프에서도 참조할 수 있는 것이다.

 

따라서 동일한 변수명을 검색하는 경우에도, 하위 스코프에 해당 변수명을 가진 변수(ⓑ)가 존재한다면 그 변수를 참조하는 것이고,

하위 스코프에 해당 변수가 존재하지 않는 경우(ⓐ) 상위 스코프 방향으로 이동하며 해당 변수를 찾는 것이다.

 

4. 함수 레벨 스코프

대부분의 프로그래밍 언어는 함수 뿐만 아니라 모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만든다.

이러한 특성을 블록 레벨 스코프라 한다.

 

하지만 var키워드로 선언된 변수는 오로지 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정한다.

이러한 특성을 함수 레벨 스코프라고 한다.

 

var x = '이규리';

if(true) {
	var x = '반원재';
}

console.log(x); // 반원재
		// var는 함수 내부에서 선언되었을 때만 지역 스코프를 갖는다.
                // if와 같이 함수 코드 블럭이 아닌 곳에서 var로 선언하는 경우 전역 변수가 된다.
                // 따라서 기존 전역변수(이규리)가 의도치 않게 변경되는 부작용을 발생시키는 것

 

참고로 ES6부터 도입된 let, const의 경우 블록 레벨 스코프를 지원한다.

따라서 더더욱 var 대신 let, const로 대체해서 사용해야 하는 이유가 되는 것.

 

5. 렉시컬 스코프

let x = '이규리';

function foo() {
    let x = '반원재';
    bar();
}

function bar() {
	console.log(x);
}

foo();
bar();

 

정적 스코프라고도 불리는 렉시컬 스코프는 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정하는 것이다.

자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따른다.

 

위 코드에서 bar 함수는 전역에서 정의된 함수이다. 따라서 전역 스코프를 갖는다. 

bar 함수는 호출할 때 호출한 곳이 어디든, 자신이 갖는 전역 스코프를 상위 스코프로 사용하게 된다.

 

따라서 foo()와 bar()을 실행하면, 둘 다 전역 스코프를 갖는 x(이규리)를 출력하게 된다.

 

Ref.

https://ko.javascript.info/closure

 

변수의 유효범위와 클로저

 

ko.javascript.info

 

728x90