Scope(스코프, 유효범위)

kirin.log·2021년 9월 22일
0

🌈 Scope(스코프, 유효범위)

어떠한 변수에 접근 가능한 유효 범위이자, Javascript 엔진이 참조 대상이 되는 식별자(Identifier)를 검색하는 규칙의 집합

  • Global Scope: 코드 전역에서 어느 곳이든 접근할 수 있음
  • Local Scope: 함수 코드 블록이 만든 스코프로 자신과 하위 함수만 사용가능

🎄 Scope 특징

var 키워드는 함수 레벨 스코프, let/const블록 레벨 스코프를 따른다.

  • 함수 레벨 스코프(Function-level scope): 함수 내 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다.
  • 블록 레벨 스코프(Block-level scope): 모든 코드블록(함수, 조건문, 반복문, try/catch 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하다.

  • var: function scope
  • let & const : block scope

JavaScript의 범위(scope)는 JavaScript에 대한 변수의 접근 가능성을 결정하는 코드의 현재 컨텍스트를 나타낸다. 두 가지 유형의 범위는 로컬(지역)전역이다.

  • 전역 변수 는 블록 외부에서 선언된 변수.
  • 지역 변수 는 블록 내부에 선언된 변수.

🌲 function-scope : var

var species = "human";  // 전역변수 선언

function transform() {
  var species = "monkey"; // 지역변수 선언(함수 scope 내)
  console.log(species);
}


console.log(species);  // human (전역변수로 인식)
transform();  // monkey (함수 내 지역변수로 인식)
console.log(species); // human (전역 변수로 인식)

❗️ var키워드로 선언된 변수 는 항상 함수 범위이다. (함수를 별도의 범위가 있는 것으로 인식한다).
따라서 이 로컬 범위 변수는 전역 범위에서 액세스할 수 없다.

🌲 block-scope : let / const
기능 블록, if문, for문while루프를 비롯한 모든 종류의 블록에서 새로운 로컬 범위가 생성

// 예시 1
if (true) {
  const message = 'Hello';  // 블록 범위(if문) 내 const 선언
}
console.log(message); // ReferenceError: message is not defined


// 예시 2 --- if 문
if (true) {
  // "if" block scope
  const message = 'Hello';
  console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError



// 예시 3  --- for 문
for (const color of ['green', 'red', 'blue']) {
  // "for" block scope
  const message = 'Hi';
  console.log(color);   // 'green', 'red', 'blue'
  console.log(message); // 'Hi', 'Hi', 'Hi'
}
console.log(color);   // throws ReferenceError
console.log(message); // throws ReferenceError



// 예시 4 --- while 문
while (/* condition */) {
  // "while" block scope
  const message = 'Hi';
  console.log(message); // 'Hi'
}
console.log(message); // => throws ReferenceError

❗️ const, let블록범위 내에서 유효하기 때문에 블록범위 밖에서 message를 호출하면 undefined 이다.

// 예시 3
let species = "human";  // 전역 변수 선언

if (fullMoon) {
  let species = "monkey";  // 블록 범위(if문 중괄호 내) 변수 선언
  console.log(`It is a full moon. Lupin is a ${species}.`);  // monkey
}

console.log(`It is not a full moon. Lupin is a ${species}.`);  // human

똑같은 경우에 var 를 사용하면 다른 결과가 나온다

var species = "human";

if (fullMoon) {
  // 블록 범위(if문) 내 새로운 변수 값을 선언한다
  var species = "monkey";
  console.log(`It is a full moon. Lupin is a ${species}.`); // monkey
}

console.log(`It is not a full moon. Lupin is a ${species}.`); // monkey

❗️ var키워드로 선언된 변수 는 항상 함수 범위만 인식하고 블록 범위는 인식하지 않기 때문에,
if문 안의 변수는 새로운 값을 할당한 것으로 인식한다.(동일한 범위에서 동일한 변수를 다시 할당한다고 인식)

🍄 변수 선언, 초기화, 사용

  • hoisting
var a = 100;
console.log(a)

var 👉 변수 선언
a = 100 👉 100이라는 값을 a에 할당하여 값 초기화
console.log(a) 👉 사용


변수가 선언되고 초기화되기 전에 사용하려고 하면 undefined.

console.log(a)  // undefined (선언하기 전에 사용부터 했기 때문에 값이 undefined)
var a = 100;

// 위 코드를 자바스크립트가 해석하는 순서
var x;  // var hoisting
console.log(x);  // 할당된 값이 없어서 undefined
x = 100;  // 이제서야 값 할당
  • nested scope
function run() {      // function scope
  const message = 'Run, Run, Run!';

  if (true) {    // block scope
    const friend = 'Bubba';
    console.log(message); // 'Run, Run, Run!'
  }

  console.log(friend); // Error
}

run();

함수 scope(run()) 안에 블록 scope (if문) 이 중첩되어 있다면,
겉에 감싸고 있는 함수 scope를 outer scope라고 하며,
안에 있는 if문 블록 scope를 inner scope 라고 한다.

let, const 는 블록범위 변수이기 때문에, if문 블록scope 안에서는 외부 변수에 접근이 가능하지만, 외부에서는 블록scope 안의 변수에 접근할 수 없다.

  • Lexical scope
function outerFunc() {
  // the outer scope
  let outerVar = 'I am from outside!';

  function innerFunc() {
    // the inner scope
    console.log(outerVar); // 'I am from outside!'
  }

  return innerFunc;
}

const inner = outerFunc();
inner();

내부 함수 범위(innerFunc())는 외부 함수 범위(outerFunc())에 포함된다.
자바스크립트는 내부 함수 범위(innerFunc())에서 외부 함수 범위(outerFunc())에 있는 변수를 읽어들인다. 이게 가능한 이유는 렉시컬 환경(lexical scoping) 때문이다.

Lexical scoping이란, 중첩된 함수 scope 내에 선언된 변수들에 대해, 내부에서 외부 범위의 변수에 접근 가능하도록 결정하는 환경적 범위를 말한다.

즉, 내부 함수는 외부 함수 범위에 접근할 수 있다. 내부 함수는 외부 함수의 범위를 동일하게 구성하고 있으며, 렉시컬 환경에 의해, 외부 함수에 선언된 변수에 접근 가능하다. (클로저)


🎄 렉시컬 스코프란(Lexical Scope)란?

함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정되는 것을 말한다.

즉, 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다는 점이다.

다른 말로, 정적 스코프(Static scope) 라 부르기도 하다.

// 렉시컬스코프 예시

var x = 1;   // global

function first() {
  var x = 10;  // local
  second();
}

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

first();    // 1
second();   // 1

값이 10, 1이 아니라 1, 1로 나온 이유는?
자바스크립트에서는 위와 같은 코드를 작성할 때, 이미 실행 단계에서 코드들의 스코프를 결정한다.
즉, 함수 second()의 상위 스코프가 무엇인지에 따라 결정된다.

자바스크립트는 렉시컬 스코프(Lexical Scope)를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.
즉, 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다는 말이다.
그렇기 때문에, second() 함수가 first() 함수 안에서 호출된 것과 상관없이 second() 함수는 global 범위에 선언되어 있으므로, global 범위에 있는 변수 x의 값 1이 두 번 출력된 것이다.


🎄 Scope Chain(스코프 체인)

현재 스코프에서 식별자를 검색할 때, 상위 스코프를 연쇄적으로 찾아나가는 방식.
실행 컨텍스트의 LexicalEnviroment 프로퍼티에서 outer 참조값을 통해 상위 스코프로 체인처럼 연결됨.

스코프 체인(Scope Chain)은 일종의 리스트로서, 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 의미 그대로 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것을 말한다.
즉, 자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들을 스코프 체인이라 할 수 있다.

  1. 현재 실행 컨텍스트의 LexicalEnvironment의 EnvironmentRecord에서 식별자 검색
  2. 없으면 outer 참조값으로 상위의 EnvironmentRecord에서 식별자 검색
  3. 이를, outer 참조값이 null(전역)일 때까지 계속 검색한다. 찾지 못하면 에러 발생

scope 참고 문서

profile
boma91@gmail.com

0개의 댓글