[Javascript] 실행 컨텍스트, 호이스팅, this

Nowod_K·2022년 9월 25일
0

Javascript의 특징

자바스크립트의 특징에 대해서 검색을 해보면 이런 특징들이 보통 나열된다.

  1. 객체 기반의 스크립트 언어이다.
  2. 동적이며 타입을 명시하지 않는 인터프리터 언어이다.
  3. 객체지향 프로그래밍과 함수형 프로그래밍을 모두 표현할 수 있다.
  4. 웹 리소스 (HTML, CSS)등을 동적으로 다룰 수 있다.

여기서 가장 중요한 특징은 무엇일까?
지금은 ES6, Node.js 등이 등장하면서 "함수형 프로그래밍, 객체지향, 객체 기반"등의 표현들이 중요해보인다.
나도 사실 지금까지는 이런 것들이 자바스크립트의 중요한 특징이라고 생각했다.

그런데 사실 이러한 특징들을 본래 자바스크립트의 특징이라기 보다는 개발 패러다임에 변화에 따라서 생긴 특징이라고 볼 수 있다.

그렇다면 자바스크립트의 진정한 특징은 무엇일까?

Javascript의 역사

1995년 넷스케이프라는 회사에서 만든 웹브라우저가 있었고, 당시 넷스케이프에서 인터랙티브한 웹을 위해
'MOCHA'라는 언어를 만들었고, 이 언어가 최종적으로 'Javascript'라는 이름을 가지게 되었다.

Javascript는 웹 리소스들을 인터렉티브하게 표현하기 위해 등장한 언어다.
DOM을 다루고, 이벤트를 핸들링하기 위해 등장한 언어라는 것에 주목하고 다시 특징을 보자.

그러면 "4. 웹 리소스 (HTML, CSS)등을 동적으로 다룰 수 있따." 이 문구가 눈에 띈다.
사실 이 특징이 Javascript의 기원인 것이다.

우리가 자바스크립트를 깊이 공부하다보면 마주하는 것들이 있다. 실행컨텍스트, 호이스팅, This의 개념이다.
이런 개념이 왜 자바스크립트에서 등장하는 것일까에 대한 해답이 바로 4번 특징이라고 생각한다.

"4. 웹 리소스 (HTML, CSS)등을 동적으로 다룰 수 있다."

정적인 리소스를 동적으로 다루기 위해서는 어떻게 해야할까?
자바스크립트를 실행할 위치를 파악하고, 동적인 이벤트들을 정의해주어야 한다..
결과적으로 코드가 쓰여질 맥락(Context)을 파악해야 한다.

그래서 자바스크립트에는 실행 컨텍스트가 존재한다고 생각한다. (뇌피셜이 들어가 있다..)

실행 컨텍스트 (Execution Context)

실행 컨텍스트는 자바스크립트 수행에 필요한 환경정보를 담은 객체이다.
이때 호이스팅이 발생되고, 환경정보를 구성하며, this 값이 설정된다.

실행 컨텍스트는 2개로 나뉘어 진다.

  1. 전역 컨테스트 (Global Execution Context)
    -> 웹의 경우 window, Node.js의 경우 Global 객체를 this에 할당한다.
  2. 함수 컨텍스트 (Functional Execution Context)
    -> 함수가 호출될 때 해당 함수에 대한 컨텍스트를 생성한다.

실행 컨텍스트는 Last in, First Out의 구조로 실행된다.

실행 컨텍스트라는 개념에 대해서 대부분 추상적인 개념이라고 생각한다.
하지만 자바스크립트가 탄생한 이유와 연관지어 생각해보면, 실행 컨텍스트는 맥락파악을 위해 필수적인 요소라는 것을 알 수 있다.

호이스팅 (Hoisting)

실행 컨텍스트가 생성 될때 호이스팅이 발생되고, 환경정보를 구성하며, this 값이 설정된다.
호이스팅이 발생된다는데, 호이스팅은 무엇일까?

대부분 호이스팅을 설명할 때 아래와 같이 설명한다.

호이스팅이란 자바스크립트에서 선언부를 상단으로 올리는 것이다.

이 설명은 자바스크립트를 어느정도 해봤다면 다 경험해본 내용이다. 자바스크립트에서는 함수를 호출부 아래에 선언해도, 호출이 되는 경험은 누구나 해봤을 것이다.

그래서 왜 선언부로 올리는 것인가??

바로 자바스크립트의 실행 컨텍스트 때문이다!!

코드가 실행되어야할 문맥을 파악하기 위해서는 문맥 근처의 변수들을 알고 있어야한다.
함수가 호출될때 생기는 함수 컨텍스트에서는 함수가 호출되는 문맥을 파악해야 하고, 이를 위해서는 함수를 구성하고 있는 선언부들을 파악해야한다. (사실 굉장히 추상적이고, 철학적인 부분이다..)
이런 범위를 "어휘적 환경(Lexical Environment)"이라고 표현한다.

호이스팅의 대상이 되는 var, let, const, function, arrow function에는 몇가지 차이점이 있다.

간단하게 설명하자면 var, function은 함수레벨 스코프이며 undefined로 초기화 된다.
let, const, arrow function은 호이스팅의 대상이기는 하나 초기화 되지 않기에 상단에서 사용하려고 한다면 에러를 발생시킨다.

var, let, const의 차이

- var

오랫동안 자바스크립트에서 사용되왔던 var는 변수를 정의할 때 사용하는 키워드이다.
var의 특징으로 인해 오랫동안 많은 개발자들이 고통받아 왔다.

  1. 함수레벨의 스코프를 가진다.
  2. var는 생략할 수 있다.
  3. 중복 선언이 된다.
  4. 호이스팅 시 undefined로 초기화된다.

특징들을 보니.. 정말 제대로 다루기가 쉽지 않다. 자칫 잘못하면, 의도와 다른 동작이 이러나기가 굉장히 쉽다.

특히 호이스팅이 될 때 var는 선언과 동시에 undefined로 초기화 된다.
undefined에 대한 에러 처리를 하지 않으면, 어디에서 오류가 발생하는지 알 수 없는 상황이 생기기도 한다.

- let

let은 ES6가 등장하면서 const와 함께 많이 쓰이고 있다.
처음에 자바스크립트를 공부할 때는 단순히 'var -> let으로 변경하면 된다'라고 생각한 적이 있었는데, 굉장히 아마추어같은 생각이었다. let은 var와 비슷하지만 다른 특징들이 있다.

  1. 블록 수준 스코프(if, for와 같은 코드 블록)를 가진다.
  2. 생략이 불가능하며, 생략하는 경우 var로 할당된다.
  3. 중복으로 선언할 수 없다.
  4. 호이스팅의 대상이긴 하지만, 초기화 되지는 않는다.

var와 비교하면 굉장히 안전해진? 특징들이라고 볼 수 있다.
그러나 사실 자바스크립트에서는 필요한 경우가 아니면 let을 쓰지 않는다.

- const

const 역시 ES6가 등장하면서 굉장히 많이 쓰이고 있다.
const는 할당된 값을 변경할 수 없다.
하지만 주소값이 할당되는 경우(배열, 객체 등)에는 배열 값을 추가하거나 하는 행위가 주소를 변경하지 않기에, 값 변경이 가능하다.
const가 많이 쓰이는 이유에는 좋은 특징들이 있다.

  1. 블록 수준 스코프(if, for와 같은 코드 블록)를 가진다.
  2. 생략이 불가능하며, 생략하는 경우 var로 할당된다.
  3. 중복으로 선언할 수 없다
  4. 호이스팅의 대상이긴 하지만, 초기화 되지는 않는다.
  5. 변경되지 않을 값이기에 안정적인 개발이 가능하다.
  6. let, var와 같이 재할당이 안되기에 성능 최적화에 도움이 된다.

this

실행컨텍스트가 생성될 때 this의 값이 할당된다.
사실 실행 컨텍스트를 알기전에 나에게 this는.. "함수호출 전에는 window 객체, 함수가 호출될 때는 함수를 호출한 객체 정도로 이해하고 있었다."
하지만 그냥 그렇게 이해할 뿐이지 this가 왜 생겼는지는 알지 못했다.

하지만 실행 컨텍스트의 생성이유와 역사를 이해하면서 this라는 것을 조금이나마 이해할 수 있었다.

this : 정의된 함수가 어떻게 발생되었는지에 따라 해당 대상의 컨텍스트를 가르키는 객체.

이게 무슨 말일까?

몇가지 예시를 보자. 옆에 달아 놓은 주석을 보면서 this를 이해해보자

[첫번째 예시]

var someValue = 'hello';
function outerFunc() {
    console.log(this.someValue); // 첫번째 : World (this: obj), 두번째 : hello (this: window)
    this.innerFunc();
}
const obj = {
    someValue : 'world',
    outerFunc,
    innerFunc : function() {
        console.log("innerFunc's this : ", this); // 첫번째 : this: obj, 두번째 : window 객체는 innerFunc가 없기에 에러 발생
    }
}
obj.outerFunc(); // 첫번째 (obj 객체가 OuterFunc를 invoke 하였기에 this는 obj가 된다.)
outerFunc(); // 두번째 (window 객체가 OuterFunc를 invoke 하였기에 this는 window가 된다.)

[두번째 예시]

var value = 1;

var obj = {
  value: 100,
  foo: function() {
    setTimeout(function() {
      console.log("callback's this: ",  this);  // setTimeout의 this는 window이다.
      console.log("callback's this.value: ",  this.value);  // window.value = 1
      function bar() {
        console.log("bar's this: ", this);  // setTimeout 안에 있으니, window 입니다.
      }
      bar();
    }, 1000);
  }
};

obj.foo();

이렇듯 실행되는 문맥에 따라 this값이 할당되게 됩니다.
단순히 외워야 되는 것이 아니라, 함수가 실행되는 원리를 따라가다 보면 자연스럽게 this 값을 이해할 수 있습니다.

후기

자바스크립트의 특징이라고 볼 수 있는 실행컨텍스트, 호이스팅, this에 대해서 알아보았는데,
참 많은 생각을 하게 되는 내용이었다.

자바스크립트가 생기게 된 역사부터 차근차근히 따라오다보니 어느정도 이해도 되지만, 깊이 갈수록 철학적인 느낌이 굉장히 많이 든다.

사실 나는 문과 출신의 개발자다. 개발이 너무 재미있지만 항상 문과적인 내용과는 동떨어져 있다고 생각했는데, 이런 언어의 특징들이 문과적인 요소에서 왔다는 생각을 하니 너무 재미있었다.

이 글을 읽는 모든 분들도 재미있었으면 좋겠다..

출처

자바스크립트는 왜 프로토타입을 선택했을까?
실행컨텍스트와 자바스크립트의 동작 원리
var let const,그리고 호이스팅

profile
개발을 좋아하는 마음과 다양한 경험을 토대로 좋은 개발자가 되고자 노력합니다.

0개의 댓글