2) 자바스크립트 작동원리 (실행 컨텍스트, 싱글스레드, 비동기)

0. 인트로

🐥 싱글스레드랑 비동기방식을 크게 실행컨텍스트 내에서 다시 정리하기!

자바스크립트는 V8엔진 내부에서 JIT컴파일러로 컴파일 된 후 인터프리팅 방식으로 언어를 해독된다.

브라우저작동원리>자바스크립트엔진

그럼 자바스크립트 코드들을 실행하는 전체적인 매커니즘은 어떤 방식일까?

1. 싱글스레드, 비동기 방식

자바스크립트는 자바스크립트엔진을 통해 싱글스레드비동기 방식으로 해독된다.

  • 싱글스레드 : 자바스크립트는 프로세스 내에서 하나의 스레드를 통해 작업을 수행한다.
  • 비동기 방식 : 비동기(Asynchronous)는 작업이 동시에 일어나는 것이 아니라 각자 별개로 일어남을 의미한다.

동기, 비동기의 개념이 많이 헷갈렸는데 동기[同期]는 같을 동자를 써서, 작업의 흐름이 모두 같은 맥락에서 일어나고, 비동기[非同期]는 그와반대로 작업이 작업의흐름과 상관없이 개별적으로 일어난다고 이해하였다!

비동기 방식을 Non-Blocking processing model(막힘이 발생하지 않음) 라고도 한다. 즉, A, B, C 작업의 흐름에서 B에서 blocking이 일어나도 C를 수행한다.

그럼 blocking은 언제 발생할까?

blocking은 요청을 보내서 시간이 걸리는 작업들에서 발생한다.

ex) 서버에서 데이터를 요청하고 받아오는 경우(AJAX), DOM 조작, setTimeout() 같은 내장 함수

  • AJAX(Asynchronous Javascript And Xml) : JavaScript를 사용한 비동기 통신, 클라이언트와 서버간에 XML 데이터를 주고받는 기술

그럼 자바스크립트는 blocking을 어떻게 막힘이 없는 비동기 방식으로 처리할까?

2. 자바스크립트 이벤트루프

js

  • heap : 자바스크립트 내의 객체, 전역 변수 등 메모리할당하는 역할
  • stack: 자바스크립트 내의 지역 변수, 콜 함수 등 메모리할당하는 역할

이벤트루프 매커니즘

  1. 자바스크립트의 함수들이 실행되면 함수들은 stack에 push(저장)되고 실행이 완료되면 pop(제거)된다.

ㄴ 싱글스레드 개념

  1. blocking (시간이 걸리는 작업들)이 들어오면 콜백함수와 함께 pop(제거)되며 web API 로 보내진다. 이 때, 다음 stack의 함수들을 실행한다.

ㄴ 비동기 방식

  1. web API 의 콜백 응답결과를 callback Queue에 저장하고, stack 에 남은 함수들이 없으면 콜백함수는 다시 stack에 push 되서 실행된다.

ㄴ 이벤트루프

이렇게 callback Queue에 작업이 들어있는지, stack이 비어있는지 끊임없이 감시하고 작업을 실행하는 것을 이벤트 루프가 담당한다.

web API 와 콜백 큐, 이벤트루프 덕분에 자바스크립트는 비동기 방식으로 작동할 수 있다.

3. 실행 컨텍스트

실행 컨텍스트 : 자바스크립트가 해석되고 실행되는 환경

execution context is an abstract concept of an environment where the Javascript code is evaluated and executed. Whenever any code is run in JavaScript, it’s run inside an execution context.

처음 코드를 읽을 때, 모든 실행 컨텍스트를 포괄하는 전역 컨텍스트 가 생성되며, 전역 컨텍스트 내에서 함수가 호출되면 함수 컨텍스트가 생성된다.

자바스크립트 엔진은 이러한 컨텍스트를 객체 형태로 관리한다.

이러한 컨텍스트 (환경) 내에서는 변수객체 , scope chain , this 가 담긴다.

3-1. 변수객체

현재 함수 내부에 있는 변수객체(변수, 함수)들이 저장된다.

let num = 0

function hello() {
  return console.log('hello')
}
// 전역 컨텍스트에서의 변수객체 : num, hello

hoisting

호이스팅 : 자바스크립트의 컴파일 단계에서 미리 변수객체들을 실행 컨텍스트에 저장하는 매커니즘

console.log(a)
func()
function func() {
  console.log('함수실행')
}
var a = '변수'
// undefined (전역 컨텍스트에 변수 a는 저장되어 있지만 초기화는 되지 않은 상태 )
// 함수실행 (전역 컨텍스트에 저장된 func 함수변체객체가 정상적으로 실행됨)

V8 자바스크립트 엔진은 자바스크립트 코드를 한번 컴파일 한 후, 인터프리팅 방식으로 코드를 읽는다.

V8 자바스크립트 엔진은 컴파일 단계에서 미리 변수들을 스캔해서 실행 컨텍스트에 변체객체들을 저장한다. 그래서 선언부의 코드들이 코드의 최상단에 올려지는 느낌을 받는다.

3-2. scope chain

전역 객체(전역 컨텍스트의 물리적 형태)를 포함한 중첩된 함수 객체(함수 컨텍스트의 물리적 형태)를 리스트의 형태로 저장한다.

해당 컨텍스트에 변수정보가 없을 시, 이 scope chain을 통해 scope를 참조한다.

scope는 이벤트루프의 call stack 처럼 함수가 호출될 때 쌓이는 개념이 아니라, 함수가 선언된 환경에서 정의되는 개념이다!

lexical scoping

lexical scoping (정적 스코프) : scope는 함수가 선언될 때 정의된다!

❓ 엔진이 scope를 호이스팅처럼 컴파일 시 결정하는 건가?
function foo() {
  console.log(x)
}

function bar() {
  var x = 15
  foo()
}

var x = 10
foo() // 10
bar() // 10

여기서 foo의 scope chain은 [전역객체, foo] 이고,

foo가 전역에서 실행되든, bar안에서 실행되든 전역변수 x = 10을 참조한다.

function day() {
  function morning() {
    function breakfast() {
      console.log('아침밥 드세요~')
    }
  }
}
// breakfast 함수 컨텍스트에서의 scope chain [전역객체, day, morning, breakfast]

3-3. this

this는 현재 실행 컨텍스트에서의 호출자를 의미한다.

const obj = {
  value: 'obj의 값',
  inner: function() {
    console.log(this.value) // 'obj의 값' 출력
  },
}
obj.inner()

브라우저 환경에서는 호출자 this에 window 전역객체가 담긴다.

💡 javascript로 브라우저 내장 기능들(web API)에 접근하고 사용할 수 있는 이유는 this(호출자)가 window여서가 아니라 scope chain에 전역객체인 window가 들어있기 때문이다!

web API : windlow.setTimeout() 같은 window내부 메소드들이나 DOM 접근 메소드 등..


Written by@taenyKim
웹 프론트엔드 공부 블로그 / Learn in Public

GitHubFacebook