자바스크립트의 실행 컨텍스트

이희제·2024년 3월 31일
0

JavaScript

목록 보기
6/6
post-thumbnail

코어 자바스크립트 2장을 정리한 내용이다.

2장은 자바스크립트의 실행 컨텍스트에 대한 내용이다.

1. 실행 컨텍스트란?

  • 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.

  • 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성 하고, 이를 콜 스택에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련있는 코드들을 실행

  • 실행 컨텍스트에 담기는 정보는 다음과 같다.

    • VariableEnvironment: 현재 컨텍스트 내의 식별자들에 대한 정보와 외부 환경 정보(LexicalEnvironment의 스냅샷으로 변경 사항 반영 X)
    • LexicalEnvironment: VariableEnvironment와 초기에는 같지만 변경 사항이 실시간으로 반영
    • ThisBinding: 식별자가 바라봐야 할 대상 객체

2. VariableEnvironment

  • VariableEnvironment 내부는 다음 2가지로 구성되어 있다.

    1. environmentRecord
    2. outer-EnvironmentReference

3. LexicalEnviroment

3-1. environmentRecord와 호이스팅

  • environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.
    • 컨텍스트를 구성 함수에 지정된 매개변수 식별자, 선언한 함수 자체, 선언된 변수의 식별자가 해당된다.
  • 컨텍스트에 변수 정보를 수집했더라도 실행하기 전이다. 즉, 자바스크립트 엔진은 코드 실행 전에 변수명들을 모두 알고 있는 것이다.

여기서 '호이스팅'이라는 개념을 확인할 수 있다. 마치 변수(정확히는 선언부)가 최상단으로 끌어올려진 것처럼 보이기 때문이다. (실제로 끌어올리지는 않는다. 그렇기 보이는 것일 뿐.)

호이스팅(hoisting) 규칙

  • 변수는 선언부와 할당부를 나눠 선언부만 끌어올려진 것처럼 보인다. 반면, 함수 선언은 함수 전체가 끌어올린 것처럼 보인다.

함수 선언문과 함수 표현식

  • 함수 선언문은 function 정의부만 존재하고 별도의 할당 명령이 없지만, 함수 표현식은 정의한 fucntion을 별도의 변수에 할당한다.
    • 보통 함수 표현식은 익명 함수 표현식을 말한다.
function a() {
  /* ... */
} // 함수 선언문. 함수명 a가 곧 변수명.
a(); // 실행 OK.

var b = function() {
  /* ... */
}; // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b(); // 실행 OK.

var c = function d() {
  /* ... */
}; // 기명 함수 표현식. 변수명은 c, 함수명은 d.
c(); // 실행 OK.
d(); // 에러!
  • 함수 선언문은 전체가 호이스팅되지만 함수 표현식은 변수 선언부만 호이스팅되는 차이점을 가지고 있다.

console.log(sum(1, 2)); // 함수 전체가 호이스팅되기 때문에 정상적으로 3이 출력된다. 
console.log(multiply(3, 4)); // 선언부만 호이스팅됐기 때문에 multiply is not a function 메세지가 뜬다.

function sum(a, b) {
  // 함수 선언문 sum
  return a + b;
}

var multiply = function(a, b) {
  // 함수 표현식 multiply
  return a * b;
};
  • 먼저 선언 후에 호출하는 것이 자연스러운 동작이기 때문에 함수 선언문은 사용을 지양하자.

3-2. 스코프, 스코프 체인, outerEnvironmentReference

스코프

  • 스코프는 식별자에 대한 유효범위이다.
  • ES5에서는 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성된다.
    • 참고로 ES6에서는 블록에 의해서 스코프 경계가 생기도록 한다. 다만, var 선언한 변수는 작용하지 않고 let, const, class, strict mode에서의 함수 선언 등에서만 작용한다.
    • 따라서 ES6에서는 함수 스코프, 블록 스코프가 구분된다.

function hello() {
  for (var i=0; i<12; i++) {
    ...
  }
  console.log(i)   // 함수 스코프가 적용되어 접근가능
}
  
hello();  //12
------------------------------------------  
function hello() {
  for (let i=0; i<12; i++) {
    ...
  }
  console.log(i) // 블록 스코프가 적용되기 때문에 i에 접근 불가
}
  
hello();  //ReferenceError: i is not defined

스코프 체인

  • outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.
    • outerEnvironmentReference는 링크드 리스트 형태를 띈다.
    • 따라서 순서대로 접근해야 하고 이런 특징으로 인해 여러 스코프에서 동일한 식별자를 선언한 경우 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하다.
var a = 1;
var outer = function() {
  var inner = function() {
    console.log(a); // undefined로 초기화된 지역변수 a 접근
    var a = 3;
  };
  inner();
  console.log(a); // 전역 변수 a 접근 
};
outer();
console.log(a); // 전역 변수 a 접근 

출력 결과는 undefined, 1, 1이다.

  • 변수 은닉화

위 예시를 기반으로 보면 inner 함수 내부에서 a 변수를 선언했기 때문에 inner 함수에서는 전역 변수 a를 접근할 수 없다. 이를 변수 은닉화라고 한다.

자바스크립트에서의 은닉화란?

객체의 데이터와 메서드를 외부에서 직접 접근하지 못하도록 제한하는 개념이다.

은닉화는 객체의 내부 구현을 외부로부터 감추고, 객체와 관련된 동작을 외부에서는 호출만 할 수 있도록 만든다.

클로저(Closure)를 통해 은닉화를 구현할 수 있다.

클로저를 이용한 은닉화 예시

function Person() {
  let age = 25; // 외부에서 접근할 수 없는 private 변수

  this.getAge = function() {
    return age;
  };

  this.setAge = function(newAge) {
    age = newAge;
  };
}

const john = new Person();
console.log(john.getAge()); // 25
john.setAge(30);
console.log(john.getAge()); // 30

이 예시에서 age 변수는 Person 함수 외부에서 직접 접근할 수 없으며, 오직 getAge와 setAge 메서드를 통해서만 접근 및 수정이 가능하다.

  • 클로저

클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.

즉, 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다


function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  return innerFunc; // 클로저
}

/**
 *  함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
 *  그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
 */
var inner = outerFunc();
inner(); // 10

실행 컨텍스트의 관점에서 보면, 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 외부함수 실행 컨텍스트 내의 활성 객체(Activation object - 변수, 함수 선언 등의 정보를 가지고 있음)는 내부함수에 의해 참조되는 한 유효하여 내부함수가 스코프 체인을 통해 참조할 수 있다.

  • 렉시컬 스코프 (정적 스코프)

렉시컬 스코프는 함수를 어디에 선언하였는지에 따라 상위 스코프가 결정되는 것을 뜻한다.

var x = 1;

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

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

foo(); // 1
bar(); // 상위 스코프는 전역 스코프이기 때문에 1 출력

전역변수와 지역변수

  • 전역 변수: 전역 공간에 선언한 변수를 뜻한다.
  • 지역 변수: 함수 내부에서 선언한 변수를 뜻한다.

코드의 안정성을 위해 전역변수 사용을 최소화하는 것이 좋다.

4. this

실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장된다.

실행 컨덱스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다.

profile
오늘만 열심히 살고 모든 걸 남기되 후회는 남기지 말자

0개의 댓글