스코프란?

참조 대상 식별자를 (identifier, 변수, 함수의 이름과 같이 어떤 대상과 구분하여 식별할 수 있는 유일한 이름) 찾아내기 위한 규칙.

식별자는 자신이 어디에 선언됐는지에 의해 자신의 유효한(다른 코드가 자신을 참조할 수 있는) 범위를 가진다.

 

스코프의 구분

전역 스코프 : 코드 어디에서든지 참조할 수 있다

지역 스코프 : 함수 코드 블록이 만든 스코프, 함수 자신과 하위 함수에서만 참조할 수 있다.

 

자바스크립트 스코프의 특징

자바스크립트는 타 언어와 달리 함수 레벨 스코프를 따른다. 

 

함수 레벨 스코프?

함수 코드 블록 내에서 선언된 매개변수와 변수는 코드 블록 내에서만 지역변수처럼 사용되고 함수 외부에서는 유효하지 않다

var a = 10;     // 전역변수

(function () {
  var b = 20;   // 지역변수
})();

console.log(a); // 10
console.log(b); // "b" is not defined

=> 변수 b는 지역변수이기 때문에 undefined가 출력된다.

 

함수 내 지역 영역에서는 전역과 지역 중 지역변수를 우선 참조한다

var x = 'global';

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

foo();          // local
console.log(x); // global

 

함수 영역 내에서 전역변수를 참조해서 전역변수의 값을 변경할 수도 있다

var x = 10;

function foo() {
  x = 100;
  console.log(x); //100
}
foo();
console.log(x); // 100

=> 전역변수의 값이 변경된 결과가 나온다

 

 

단, ECMAscript6에서 도입된 let, const 를 사용하면 블록 레벨 스코프를 사용할 수 있다.

 

블록 레벨 스코프?

코드 블록 ({...}) 내에서 유효한 스코프

함수내부에서만 유효했던 것과 비슷하게, 블록 안에서 선언하면 자신을 정의하는 블록과 하위 블록에서만 접근이 가능하다.

 

 

전역 스코프

전역에 변수를 선언하면 어디서든지 참조할 수 있는 전역 스코프를 가진 전역 변수가 된다.

-> 전역 객체(Global Object) window의 프로퍼티이다.

의도치 않은 재할당에 의한 상태 변화로 코드를 예측하기 어려워지므로 사용을 억제해야 한다.

 

 

렉시컬 스코프

렉시컬 스코프는 함수를 어디에 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다.

자바스크립트는 렉시컬 스코프를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.

var x = 1;

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

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

foo(); // 1
bar(); // 1

=> bar()가 어디서 호출되었는지는 스코프 결정에 아무런 의미를 주지 않는다. bar는 전역에 선언되었기 때문에 

bar의 상위 스코프는 전역 스코프이고 전역 변수 x의 값 1을 두 번 출력하게 된다.

 

 

암묵적 전역(implicit global)

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 식별자
  y = 20;
  console.log(x + y);
}

foo(); // 30

선언하지 않은 식별자에 값을 할당하면 전역객체의 프로퍼티가 된다.

y = 20 을 window.y = 20 으로 해석해서 y는 마치 전역 변수처럼 동작한다. 이러한 현상을 암묵적 전역이라고 한다.

// 전역 변수 x는 호이스팅이 발생한다.
console.log(x); // undefined
// 전역 변수가 아니라 단지 전역 프로퍼티인 y는 호이스팅이 발생하지 않는다.
console.log(y); // ReferenceError: y is not defined

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 변수
  y = 20;
  console.log(x + y);
}

foo(); // 30

=> 단지 전역 객체의 프로퍼티로 추가되었을 뿐이기 때문에 변수가 아니다. 그러므로 변수 호이스팅은 발생하지 않는다.

 

 

스코프 체인

현재 스코프에서 식별자를 검색할 때 상위 스코프를 연쇄적으로 찾아나가는 방식을 말한다.

function a(){
     var a = 1;              
     function b(){
      	console.log(a);  
     }
     b();
}
a();

b() 함수 내에서 변수 a를 탐색하기 시작하는데,

만약 b() 함수에 변수 a가 없다면 함수 b()를 감싸고 있는 외부함수 a()를 탐색하게 된다.

이때 변수 a가 존재하면 a를 참조하게 되고 없다면 계속적으로 감싸고 있는 상위 함수를 탐색합니다.

결국 찾지 못하고 root인 Global Object 실행 컨텍스트까지 와서 a 가 없다면 VM500:1 Uncaught ReferenceError: a is not defined라는 에러를 출력한다.

함수가 생성(선언)될 때마다 렉시컬 환경이 만들어지고 그 안에 outer 참조값이 있다. 바로 이 outer 참조값이 상위 스코프의 렉시컬 환경을 가리키기 때문에 이를 통해 체인처럼 연결되는 것이다.

(= 함수가 생성되면 Execution Context가 생성되고, 그리하여 Lexical Environment 안의 outer Lexical Enviroment가 상위 Environment Record와 연결되면서 내부 함수 정보를 탐색할 수 있게 된다.)

 

즉, 내부 스코프에서 외부 스코프로 찾아가는 방식이다.

 

 

 

참고:

https://tyle.io/blog/54

https://poiemaweb.com/js-execution-context

https://github.com/baeharam/Must-Know-About-Frontend/blob/main/Notes/javascript/scope.md

 

'엘리스 ai 트랙 > FE' 카테고리의 다른 글

IIFE (즉시 호출 함수 표현식)  (0) 2022.02.28
var vs let vs const  (0) 2022.02.21
BFC (Block Formatting Context)  (0) 2022.02.05
z-index  (0) 2022.02.02
표준모드(standards mode)와 호환모드(quirks mode)  (0) 2022.01.23

+ Recent posts