스코프(유효 범위)

이번에는 스코프가 무엇인지 알아보고 이를 문제에 적용하여 이해하며 풀어보자!


🔎 스코프란 뭘까?

모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)가 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위를 말한다.

function add(x, y) {
	console.log(x, y); // -> 2 5
}

add(2,5);

위 예제로 더 자세히 살펴보자
add라는 함수 내에서 선언된 매개변수 x ,y는 함수 내에서 출력했을 때는 인수에 의해 2와 5라는 값이 잘 출력된다.

하지만,

function add(x, y) {
	
}

add(2,5);

console.log(x, y); // -> ReferenceError: x is not defined

add함수에서 선언한 매개변수를 함수 밖에서 사용하게되면 에러를 마주하게된다.

즉, 매개변수는 함수 내부에서만 참조 가능하다.
다시말해 매개변수의 스코프(유효 범위)는 함수 몸체 내부이다!

✏️ 같이 풀어보자!

var x = 'global';

function foo() {
	
	var x = 'local';
	console.log(x); // 1번 : ?
}

foo();

console.log(x); // 2번 : ?

답 부터 말하자면 1번에는 local이 출력되고 2번에는 global이 출력된다.

1번에 답이 왜 global이 아닐까?
foo 함수 내에 local 값을 가진 변수 x가 선언되지 않았더라면 답이 global이었을 것이다.

하지만, foo 함수 내부에서 x를 출력해야하기 때문에 변수x의 스코프를 참고하여 값이 local이 된 것이다!

그렇다면 2번은 왜 global일까?
당연하다! 2번의 변수x는 foo 함수 밖에 있으므로 함수의 스코프를 벗어났기 때문에 2번의 x의 스코프(함수 밖)에의해 답은 global이 되는 것이다.

⚠️ var 키워드 주의!

var x = 'global';

function foo() {
    
	var x = 'local';
    var x = 1;
	console.log(x); // 1번
}

foo();

console.log(x); // 2번

위 예제에서 변수 x 하나를 더 추가했다.
1번의 답이 뭐가될까?
답은 1이다!

var 키워드는 let과 const와 달리 같은 스포크 내에서 중복 선언이 가능하다.
따라서, var 키워드를 사용해 변수를 선언하면 의도치 않게 변수 값이 재할당되어 부작용이 발생할 수 있다!

같은 스코프 내에서 변수 선언을 할때는 var 키워드는 지양하도록 하자...


🔎 스코프의 종류

스코프의 종류는 크게 전역지역으로 나눠진다.

  • 전역 : 코드의 가장 바깥 영역

    • 전역 스코프
    • 전역 변수
  • 지역 : 함수 몸체 내부

    • 지역 스코프
    • 지역 변수

위에서 예제로 살펴봤듯이, 변수는 자신이 선언된 위치(전역 또는 지역)에 의해 자신의 스코프가 결정된다.

다시말해, 전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수가 되는 것이고, 지역에서 선언된 변수는 지역 스코프를 갖는 지역 변수가 되는 것이다!

위에서 봤던 예제를 다시 보자

var x = 'global';

function foo() {
	
	var x = 'local';
	console.log(x); // 1번 : ?
}

foo();

console.log(x); // 2번 : ?

global값을 갖는 변수 x는 전역 변수가 되는 것이고, local값을 갖는 변수 x는 함수 내에 있기 때문에 지역 변수가 되는 것이다.


✏️ 같이 풀어보자!

var x = "global x";
var y = "global y";

function outer() {
	var z = "outer's local z";

	console.log(x); // 1번
	console.log(y); // 2번
	console.log(z); // 3번
	
	function inner() {
		var x = "inner's local x";
		
		console.log(x); // 4번
		console.log(y); // 5번
		console.log(z); // 6번
	}
	
	inner();
}

outer();


console.log(x); // 7번
console.log(z); // 8번

우선 답 부터 말하자면 다음과 같다

  • 1번: global x
  • 2번: global y
  • 3번: outer's local z
  • 4번: inner's local x
  • 5번: global y
  • 6번: outer's local z
  • 7번: global x
  • 8번: ReferenceError

1번부터 천천히 살펴보자!

1번은 outer 함수 내에 선언된 x가 없으므로 전역에 선언된 변수 x를 사용해야한다.

왜 inner 변수에서 선언된 변수 x를 사용하면 안될까?
참조에도 참조 순서라는 것이 있다. 이는 조금 뒤에서 다시 얘기해보자!

2번역시 outer 함수 내에 선언된 값이 없다. 따라서 전역 변수를 사용하여 답은 global y가 되는 것이다.

3번은 outer 함수 내에 선언된 값이 존재하므로 스코프에 의해 답이 outer's local z가 되는 것이다.

4번은 inner 함수 내에 선언된 값이 존재하므로 스코프에 의해 답이 inner's local x 가 되는 것이다.

5번은 inner 함수 내에 선언된 값이 없으므로 전역 변수를 참조하여 답이 global y가 된다!

6번은 inner 함수 내에는 선언된 값이 없으나 하나 윗 단계에 있는 outer 함수 내에 지역 변수 z가 존재하므로 이를 참조하여 답이 outer's local z 가 된 것이다.

7번은 완전히 함수 밖에 존재하므로 전역 변수 x를 참조하여 답이 global x가 된다.

8번은 에러가 난다!
변수 z는 함수 내에 존재하므로 지역 변수이다. 따라서 전역에서는 참조할 수 없다!


🔎 스코프 체인

방금 전 살펴봤던 예제처럼 함수 내에 또 다른 함수가 존재하는 것을 함수의 중첩 이라고 한다.

중첩된 함수에서는 더 안쪽에 존재하는 함수를 내부 함수, 더 바깥쪽에 존재하는 함수를 외부 함수 라고 한다.

지금까지는 전역과 지역으로 나눴지만, 중첩 함수에 의해서 지역 내에서도 inner와 outer로 나뉘기 때문에 계층적 구조를 갖게 된다.

이때, inner 함수는 outer 함수의 하위 스코프가 되며, 하며 반대로 outer 함수는 inner함수의 상위 스코프가 된다.


다음 그림과 같이, 스코프는 하나의 계층적 구조로 연결되어 있으며, 최상위 스코프는 항상 전역 스코프이다.

이처럼 스코프가 하나의 체인처럼 계층적으로 연결된 것을 스코프 체인 이라고 부른다!

⚠️ 참조 주의!

JS 엔진이 변수를 참조할 때는 스코프 체인에 의해 변수를 참조하는 코드의 스코프에서 시작해서 상위 스코프 방향으로 이동하며 변수를 검색 한다.

따라서, 상위 스코프에서 선언한 변수를 하위에서 참조할 수는 있지만,
하위에서 선언한 변수를 상위 스코프에서 참조할 수는 없다!

아까 살펴본 예제에서 상위에 존재하는 변수가 하위에서 선언한 변수를 참조하지 못해 에러가 난 이유이다


😶‍🌫️마치며...

이번 글에서는 스코프와 지역 변수 그리고 전역 변수에 대해서 알아봤다

예제를 통해 알아보며 스코프의 정의와 지역 변수와 전역 변수의 참조 방식(?)에 대해서 명확히 알 수 있었다.

코드가 복잡해지면 변수의 참조가 헷갈릴때가 많은데, 스코프 체인을 생각하며 조금 더 체계적으로 코드를 볼 수 있을 것 같다.


참고 자료

profile
아는만큼 보이는🌱👀

0개의 댓글