Scope

movie·2022년 7월 31일
0

☁️ 스코프란 뭘까?

  • 유효범위
  • 식별자를 찾아내기 위한 규칙
  • 식별자는 자신이 어디에서 선언됐는지에 의해 다른 코드가 자신을 참조할 수 있는 범위를 갖는다.
var x = 'global'; // 1️⃣

function foo () {
  var x = 'function scope'; // 2️⃣
  console.log(x);
}

foo(); // ?
console.log(x); // ?

1️⃣ 전역에 선언되었기 때문에 어디에서든 참조할 수 있다.

2️⃣ foo 함수 안에서 선언되었기 때문에 foo 함수 내부에서만 참조 가능하다.

⇒ 이러한 규칙을 스코프라고 한다.

만약 스코프가 존재하지 않는다면, 같은 식별자 이름은 충돌을 발생시키기 때문에 프로그램 내에서 단 하나의 식별자 이름만 쓸 수 있다. 컴퓨터로 생각해보면 컴퓨터에 디렉토리가 존재하지 않았다면 같은 이름의 파일은 하나만 존재할 수 있다.

⇒ 스코프도 이와 같이 식별자 이름의 충돌을 방지한다.



☁️ JS에서의 스코프

  • 자바스크립트 관점에서의 스코프
    • 전역 스코프 (global scope) : 코드 어디에서나 참조 가능
    • 지역 스코프 (local scope / function level scope) : 함수 코드 블록 스코프로 함수내에서만 참조 가능
  • 자바스크립트의 변수 관점에서의 스코프
    • 전역 변수 (global variable) : 전역에 선언된 변수로 어디서나 참조 가능
    • 지역 변수 (local variable) : 함수 내에 선언된 변수로 함수 내에서만 참조 가능


☁️ JS의 스코프에 대한 여러 특징

1️⃣ 변수는 선언 위치에 따라 스코프를 가진다.

  1. 전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수이고,
  2. 함수 내부에서 선언된 변수는 지역 스코프를 갖는 지역 변수가 된다.

2️⃣ 다른 언어와 달리 [블록 레벨 스코프]를 따르지 않고 [함수 레벨 스코프]를 따른다.

  • c언어에서는 블록 레벨 스코프를 따르기 때문에 if문 내에 선언된 x는 if문 코드 블록 내에서만 유효하다.
// c언어 

int main(void) {
  // block-level scope
  if (1) {
    int x = 5;
    printf("x = %d\n", x);
  }

  printf("x = %d\n", x); // use of undeclared identifier 'x'

  return 0;
}
  • 하지만 자바스크립트에서는 함수 레벨 스코프를 따르기 때문에 if문내에 선언된 x라도 if문 밖에서 유효하다.
// javascript의 var

function main() {
	if (true) {
		var x = 5;
		console.log(x);
	}

	console.log(x); // 5
}

ES6에서 도입된 let 같은 경우는 블록 레벨 스코프를 따른다. 즉, let을 사용하면 c언어 예시와 같은 현상이 나타난다.


3️⃣ 우선 참조

  • 전역 영역에서는 전역변수만을 참조 가능하며,
  • 함수 내 지역 영역에서는 전역과 지역 변수 모두 참조 가능하다.
    • 하지만 변수명이 중복된 경우, 지역변수를 우선하여 참조한다.

4️⃣ 스코프 체인

  • 식별자를 찾는 일련의 과정


☁️ 전역 변수

  • 전역에 변수를 선언하면 전역 스코프를 갖는 전역 변수가 된다.
  • var 키워드로 선언한 전역 변수는 전역객체 window의 프로퍼티이다.
  • 자바스크립트는 타 언어와 달리 특별한 시작점이 없기 때문에 전역변수를 선언하기 쉽다.
    • 자바스크립트는 코드가 나타나는 즉시 해석되고 실행된다.
    • 전역 변수를 선언하기 쉬우면 여러 문제가 있는데
      • 변수 이름이 중복될 수 있으며,
      • 의도치 않은 재할당으로 인해 코드 예측을 어렵게 한다.

함수 레벨 스코프는 어떤 문제가 있을까?

if (true) {
  var x = 5;
}

console.log(x); // 5

자바스크립트에서의 var 는 함수 레벨 스코프를 따르기 때문에 코드 블록 내에서 선언되었다 하더라도, 함수 블록이 아니라면 전역 스코프를 갖는다. 즉, 전역 변수로 선언될 확률이 높다.



☁️ 동적 스코프 (Dynamic scope) vs 정적 스코프 (Static scope)

var x = 1;

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

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

foo(); 
bar();

위 코드의 실행 결과는 bar 함수의 상위 스코프가 전역(1️⃣)이냐 foo(2️⃣)냐에 따라 값이 달라진다.

1️⃣ : 만약 bar의 상위 스코프가 전역이라면 이는 bar가 선언된 시점을 중점으로 스코프를 결정한 것이다.
2️⃣ : 만약 bar의 상위 스코프가 foo라면 이는 bar가 실행된 시점을 중점으로 스코프를 결정한 것이다.

1️⃣의 방식을 정적 스코프 (static scope) 또는 렉시컬 스코프 (lexical scope)라고 하고,
2️⃣의 방식을 동적 스코프 (dynamic scope) 라고 한다.

자바스크립트를 포함한 여러 언어들이 정적 스코프 • 렉시컬 스코프를 따른다.
즉, 함수를 호출한 위치가 아닌 선언한 위치에 따라 스코프를 결정한다.

☁️ 암묵적 전역 (Implicit global)

var x = 10; // 전역 변수

function foo () {
  y = 20; // 선언되지 않은 식별자 

  console.log(x + y);
}

foo(); // 30

foo안의 y는 선언되지 않은 변수로 y = 20 을 실행하면 참조 오류가 날 것이라고 예상할 수 있다.

하지만 예상과 다르게 y는 선언된 변수처럼 사용이 된다.

자바스크립트 엔진은 변수 y에 값을 할당하기 위해 먼저 스코프 체인을 통해 선언된 변수인지 확인한다.

이때 스코프 어디에서도 변수 y의 선언을 찾을 수 없으므로 참조 에러가 발생해야 하지만, 자바스크립트 엔진은 y = 20을 window.y = 20으로 해석하여 프로퍼티를 동적 생성한다.

이런 이유로 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작한다.

이러한 현상을 암묵적 전역(implicit global)이라 한다.



참고

profile
영화보관소는 영화관 😎

0개의 댓글