본문 바로가기

FrontEnd/JavaScript

[JS] Scope 스코프 with(deep dive)

자바스크립트 Scope 이해하기 

자바스크립트 스코프는 총에서 쓰는 스코프와 개념이 비슷합니다. 

즉 자신이 보이는 유효 범위를 나타내 주는 개념입니다! 

코드로 예시를 보여드리겠습니다.

Example  

function add(x, y) {
	console.log(x, y);
	return x + y;
}

add(2, 5);

console.log(x, y); // x is not defined

위 예시를 보면 함수 매개변수는 블록 안에서만 접근이 가능합니다. 

즉 매개변수 유효 범위 (스코프)는 함수 몸체 내에서만 접근이 가능합니다! 

 

유요한 스코프 바깥에서 매개변숭에 접근하면 선언되지 않았다고 y 가기 전에 x에서 바로 not defined 오류가 뜹니다.

 

변수는 자신의 선언된 위치에 따라서 유효범위가 결정된다! 
var x = "global";

function scope() {
	var x = "local";
	console.log(x);
}

scope(); // local

console.log(x); // global

변수 스코프는 자신이 선언된 위치서부터 스코프를 타고 올라가는데 위 예시에는 scope() 함수가 실행되고 , scope() 함수에는 var x 가 선언되어 있고, console.log(x)는 자신 스코프 안에 있는 x가 있는지 찾아보고 있으면 '어? 발견했다 x!! ' 하면서 scope() 안에 x인 local이 찍히게 됩니다. 

 

그러고 나서 바깥 console.log(x)는 자신이랑 동등한 위치인 스코프에서 변수 x를 찾고 global이 찍힙니다.! 

🔑 정리

똑같은 이름의 x 변수중 어느것을 참조해야 할 것인지 결정해야 하는데 , 이를 식별자 결정 이라고 합니다.

scope() 내에서 식별자는 local이고 , 전역에서의 x 식별자는 global입니다.! 

즉 이름이 같아도 스코프 가 다르면 다른 변수다. ! 

 

개념 

* 렉시컬 환경: 코드가 어디서 실행되며 , 주변에 어떤 코드가 있는지를 확인 

* 실행 컨텍스트콘텍스트 : 렉시컬 환경을 구현한 것 (모든 코드는 실행 콘텍스트에서 평가되고 실행된다.) 

🤔 만약에 scope() 안에 var x 가 없다면 ?? 

var x = "global";

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

scope(); // global

console.log(x); // global

위 코드 결과로는 global 이 두 번 찍히게 됩니다. 

이유는?  순서로 한번 정리해 보겠습니다.

 

1. scope() 함수가 실행된다.

2. scope() 함수 내에서 console.log(x)가 실행된다.

3. x 변수를 출력하기 위해서 x를 찾습니다. 없으면? 그 상위 스코프를 타고 올라갑니다.

4. 전역에 있는 x 변수 'global을 발견' console.log(x)에 전역 변수 x를 출력합니다. 

5. 전역에 있는 console.log(x) global을 출력하고 끝납니다. 

 

식별자는 유일해야 한다! 

스코프 안에 식별자는 유일해야 합니다!! 

function scope() {
  let x = 1; // Cannot redeclare block-scoped variable 'x'

  let x = 2; // Cannot redeclare block-scoped variable 'x'
}

scope()

한 스코프 내에서 똑같은 변수를 두 번 선언할 수 없습니다. ex) const , let 

위 예시 코드는 실행하면 에러가 발생합니다.

 

근데 예외 변종 같은 녀석이 있습니다. 

 

그 이름은 바로 var 

var는 중복 선언이 허용됩니다. 그래서 의도치 않게 위에서 이미 선언된 변수를 다시 재 선언해도 

오류가 발생하지 않습니다. 이는 다른 사람들과 협업할 때 다른 협업자가 이미 똑같은 변수를 선언했을 때 

오류를 발생시키지 않고 덮어씌워지는 치명적인 실수가 발생할 수 있습니다. 

function scope() {
	var x = 1;
	var x = 2;
	console.log(x);
}

scope();

위 코드는 오류를 발생시키지 않는 치명적인 단점이 있습니다.!! 

var 보단 let , const를 쓰는 것을 생활화합시다 ~ 

 

스코프의 종류

 

* 전역

 - 코드의 가장 바깥 영역 (전역 스코프)

* 지역

 - 함수 몸체 내부 (지역 스코프)

🛠 Example 

let x = "global x";
let y = "global y";

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

	console.log(x);
	console.log(y);
	console.log(z);

	function inner() {
		let x = "inner's local x ";
		console.log(x);
		console.log(y);
		console.log(z);
	}

	inner();
}

outer();
console.log(x);
console.log(y);

내가 예측한 순서  console.log()  

1. global x , global y , outer's local z

2. inner's local x , global y , outer's local z

3. global x , global y 

 

실행 순서 

1. outer() 함수 실행 

 - local z 선언 

 - console.log() 실행 , 각각 x , y , z 접근 

 - x , y는 스코프에 없기 때문에 , 스코프 체인을 타고 전역 스코프로 올라가서 전역 x , y 참조 

 출력

 global x
 global y
 outer's local z

2. inner() 함수 실행 

 - local x 선언 

-  console.log() 실행 , 각각 x , y , z 접근

출력

inner's local x
global y
outer's local z

3. 전역 console.log() 실행 

출력

 global x
 global y

📌 중요

1. 지역 변수는 전역 변수 접근이 가능하다.

2. 전역 변수는 지역변수 접근이 불가능하다.

 

지역변수가 전역 변수에 접근 가능한 이유! 

-> 지역 변수는 유효 스코프 내에 참조하는 변수가 없을 시 스코프를 타고 바깥 스코프에 참조하려는 변수가 있는지 확인합니다! 이런 식으로 스코프를 타고 쭉쭉 올라가는 걸  스코프 체인이라고 합니다.

 

 🎇 자바스크립트는 스코프에서 시작해서 상위 스코프 방향으로 계속 찾아간다!  🎇

렉시컬 환경

* 스코프 체인은 실행 콘텍스트의 렉시컬 환경을 단방향으로 연결한 것이다. 

 

스코프 체인에 의한 함수 검색
function foo() {
	console.log("global function foo");
}

function bar() {
	function foo() {
		console.log("local function foo");
	}
	foo();
}

bar();

실행결과 예상해보기!! 

1. bar()  함수가 실행된다.

2. bar() 함수 내에서 foo() 함수가 실행된다. 

3. bar() 함수내에 foo() 함수가 실행된다

4. local function foo 가 찍힌다. 

 

✨ 요시 ~ 정답 

foo()가 실행될 때 제일 가까운 스코프에 있는 foo() 함수가 실행됩니다! 

렉시컬 스코프 
var x = 1;

function foo() {
	var x = 10;
	bar();
}

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

foo();
bar();

실행결과 예상해보기!! 

10 

👎 땡! 

틀렸습니다. 

제가 답을 저렇게 생각한 이유는 bar() 함수가 실행될 때 가장 가까운 변수 x를 참조해서 10이 찍힌다고 생각했는데 

답이 아니었다! 

 

🎈 렉시컬 스코프 정리

1. 함수를 어디서 호출했는지 따라 함수의 상위 스코프를 결정한다. (동적 스코프)

2. 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정한다.  (정적 스코프 , 렉시컬 스코프)

 

자바스크립트는 렉시컬 스코프를 따른다!  즉 어디서 정의했는지에 따라서 상위 스코프가 결정된다. 

위 코드에서는 bar() 함수가 정의된 곳이 전역이기 때문에 스코프가 전역으로 잡힌다. 

그래서 결과가 

1

1

이렇게 찍힌 것이다. 

나는 이때까지 정적 스코프로 이해했나 보다... 😅

 

 

Reference : modern javascript deep dive