Skip to main content

13장 스코프(Scope)

13.1 스코프란?

스코프(scope; 식별자 (참조할 수 있는) 유효범위)

  • 모든 식별자(변수이름, 함수이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위가 결정된다.
  • 식별자 결정(identifier resolution): 자바스크립트 엔진이 이름이 같은 두 개의 변수 중에서 어떤 변수를 참조해야 할 것인지 결정. 이때 스코프를 통해 어떤 변수를 참조할지 결정함. ( 만약 스코프가 없다면 동일명을 갖는 변수는 충돌→ 전체 프로그램에서 딱 1개만 사용할수밖에 없을것) ⇒ so, 스코프 == 식별자를 검색할 때 사용하는 규칙 ==이름공간(name space) (식별자인 변수 이름의 충돌방지⇒ 현 스코프와 다른 스코프에서는 같은 이름의 변수 사용가능)
  • 자바스크립트 엔진은 코드를 실행 할 때 코드의 문맥(context)를 고려.
  • 식별자: 어떤 값을 구별하여 식별해낼 수 있는 고유한 이름.
  • unique: 유일해야하는 값 ⇒ 식별자의 변수이름은 중복 불가. ⇒name binding: 하나의 값은 유일한 식별자에 연결되어야 한다.
  • 렉시컬환경(lexical environment): 코드가 어디에서 실행되며 주변에 어떤 코드가 있는지
  • 코드의 문맥(context): 렉시컬 환경으로 구성됨.
  • 실행 컨텍스트(execution context) : 렉시컬 환경을 구현한 것
  • 모든 코드는 실행 컨텍스트에서 평가되고 실행된다.
function foo() {
var x = 1;
//var 키워드로 선언된 변수는 같은 스코프 내에서 중복선언이 허용된다.
// 이는 의도치 않게 변수값이 재할당되어 변경되는 문제를 야기한다.
//아래 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다.
var x = 2;
console.log(x); //2
}
foo();

function fooo() {
let x = 1;
// let 또는 const 키워드로 선언된 변수는 같은 스코프 내에서 중복선언을 허용하지 않는다.
let x = 2; // SyntaxError: Identifier 'x' has already been declared
}
fooo();

13.2 스코프의 종류

:: 자신이 선언된 위치( 전역 또는 지역 )에 따라 유효범위(스코프)가 결정된다.

  1. 전역(global): 코드(함수)의 가장 바깥 영역.
  • 전역 스코프(global scope)를 갖는 전역 변수(global variable)
  • 전역변수: 어디서든지 참조할 수 있다. = 함수 내부에서도 참조 가능
  1. 지역(local): 함수 몸체 내부
  • 지역 스코프(local scope)를 갖는 지역 변수(local variable)
  • 지역변수: 자신이 선언된 지역 스코프와 하위 지역(중첩함수) 스코프 에서만 참조할 수 있다.
var x = "global x";

function outer() {
var z = "outer's local z";
console.log(x); // "global x"
console.log(z); // "outer's local z"

function inner() {
var x = "inner's global x";
console.log(x); //"inner's global x"
console.log(z); // "outer's local z"
}
inner();
}
outer();
console.log(z); // ReferenceError: z is not defined

inner’함수 내부에 선언된 x변수’ 이외에 ‘동일한 이름의 전역변수 x’존재. 이때 inner함수 내부에서 x를 참조할 경우? inner’함수 내부에 선언된 x변수가 참조됨.

⇒ identifier resolution :자바스크립트 엔진이 “스코프 체인”을 통해 참조할 변수를 검색했기 때문.

13.3 스코프 체인(scope chain): 스코프가 계층적으로 연결 된것

  • 함수의 중첩: 함수 몸체 내부에서 함수가 정의된 것.

  • 중첩함수(nested function): 함수 몸체 내부에서 정의한 함수.

  • 외부함수(outer function): 중첩함수를 포함하는 함수.

  • 함수는 중첩될 수 있다 ⇒ 함수의 지역 스코프도 중첩될 수 있다 즉, 스코프가 함수의 중첩에 의해 계층적 구조를 갖는다(계층적으로 연결된다). 외부함수의 지역 스코프 == 중첩함수의 상위 스코프

  • 모든 스코프는 하나의 계층적 구조로 연결⇒ 모든 지역 스코프의 최상위 스코프는 전역 스코프이다.

  • 실행 컨텍스트의 렉시컬환경을 단방향으로 연결(chaining)한것

  • 변수를 참조할 때 자바스크립트 엔진은 스코프체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여→ 상위 스코프방향으로 이동하며 선언된 변수를 검색(identifier resolution)한다. ⇒이를 통해, 하위스코프에서 상위스코프에서 선언한 변수를 참조할 수 있음.

  • [1] 스코프 체인에 의한 변수 검색 : 상위스코프에서 유효한 변수는 하위스코프에서 자유롭게 참조할 수 있지만, 하위 스코프에서 유효한 변수를 상위스코프에서 참조할 수 없다.

  • 스코프체인은 물리적 실체로 존재한다.

  • 자바스크립트 엔진은 코드(전역 코드와 함수코드)를 실행하기에 앞어 렉시컬환경(자료구조)을 실제로 생성한다. 변수선언이 실행되면 변수식별자가 자료구조에 키로 등록되고, 변수할당이 일어나면 자료구조의 변수식별자에 해당하는 값을 변경한다. 변수검색도 자료구조 상에서 이뤄진다.

  • 전역 렉시컬환경: 코드 로드시 곧바로 생성

  • 함수의 렉시컬 환경: 함수 호출 시 곧바로 생성

//전역함수
function foo() {
console.log("global function");
}

function bar() {
//중첩함수
function foo() {
console.log("local function");
}

foo(); // 1번: "local function"
}

bar();

함수 선언문으로 함수를 정의 ⇒ 런타임 이전에 함수 객체가 먼저 생성됨.

자바스크립트 엔진은 함수 이름과 동일한 이름의 식별자를 암묵적으로 선언 → 생성된 함수 객체를 할당

1번에서 foo함수를 호출? 함수호출을 위해 먼저 함수를 가리키는 식별자 foo를 검색

함수(함수 객체)도 식별자에 할당 되기 때문에 스코프를 갖는다.

⇒ so, Scope ==”식별자를 검색하는 규칙”

13.4 함수 레벨 스코프

  • 코드블록이 아닌 함수에 의해서만 지역스코프가 생성된다.
  • C나 JAVA 등을 비롯한 대부분의 프로그래밍 언어 함수 몸체만이 아니라 모든 코드블록(if, for, while, try/catch 등)이 지역 스코프를 만든다. ⇒ 블록 레벨 스코프(Block level scope)
  • var 키워드로 선언된 변수는 오로지 함수의 코드블록(함수몸체)만을 지역스코프로 인정 ⇒ 함수 레벨 스코프(Function level scope)

ES6에서 도입된 let, const 는 “블록 레벨 스코프”를 지원한다.

var x = 1;

if (true) {
// var키워드로 선언된 변수는 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정한다.
// 함수 밖에서 var 키워드로 선언된 변수는 코드블록 내에서 선언되었다 할지라도 모두 전역변수이다.
// 따라서 x는 전역변수이다. 이미 선언된 전역변수 x가 있으므로, x변수는 중복선언에 해당한다.
// 이는 의도치 않게 변수 값이 변경되는 부작용을 발생시키다.

var x = 2;
}

console.log(x); // 2

var i = 10;
for (var i = 0; i < 5; i++) {
console.log(i); //0 1 2 3 4
}

console.log(i); //5 왜?

13.5 렉시컬 스코프 😀

var x = 1;

function foo() {
var x = 10;
bar();
// bar함수 호출
// 호출된곳 상관 없이 언제나 자신이 기억하는 전역스코프를 상위스코프로 사용.
}

//전역에서 정의된 함수
// 전역 코드 실행 전 먼저 평가되어 함수객체 생성
// 이때 생성된 bar함수 객체는 자신이 정의된 스코프(전역스코프)를 기억
function bar() {
console.log(x);
}

foo(); // 10? nope 1!!
bar(); // 1

bar 함수의 상위 스코프가 무엇인지에 따라 결정된다.

  1. 동적 스코프(dynamic scope):함수를 어디서 호출했는지에 따라 함수의 상위스코프를결정
  • bar함수의 상위스코프: foo함수의 지역스코프와 전역스코프
  • 함수를 정의하는 시점에는 함수가 어디서 호출되는지 알수 없음⇒ 함수 호출시점에 동적으로 상위스코프를 결정해야함
  1. 정적 스코프(static scope; 렉시컬스코프; lexical scope): 함수를 어디서 정의했는지에 따라 함수의 상위스코프를 결정
  • bar함수의 상위스코프: 전역 스코프
  • 상위 스코프가 동적으로 변하지 않음
  • 함수 정의가 평가되는 시점에 상위스코프가 정적으로 결정됨.
  • 자바스크립트를 비롯한 대부분의 프로그래밍언어는 렉시컬스코프를 따름
  • 클로저와 깊은 관계.

자바스크립트- 렉시컬 스코프를 따름 - 함수 정의가 실행될 때 정적으로 결정

함수 정의(함수 선언문 또는 함수 표현식)가 실행되어 생성된 함수 객체가 함수 정의가 실행될 때 정적으로 결정된 상위 스코프를 기억함.

함수가 호출 될 때마가 함수의 상위스코프를 참조할 필요가 있기 때문