기존 웹페이지는 HTML, CSS로만 이루어진 동적인 컨텐츠만 있었지만, 시간이 지나면서 역동적인 컨텐츠를 나타내기 위한 웹페이지의 보조적인 기능을 수행하기 위해 브라우저에서 동작하는 경량 프로그래밍 언어를 도입하기로 했습니다. 그렇게 만들어진 것이 자바스크립트입니다.
그래서 개발자가 작성한 코드를 처리할 수 있는 스레드와 콜 스택이 하나씩으로만 구성된 싱글 스레드 기반의 언어로 만들어졌다고 생각합니다.
스레드는 프로세스 안에서 실행되는 흐름의 단위입니다. 일반적으로 하나의 프로그램에는 하나의 스레드만이 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수도 있습니다.
싱글스레드는 한번에 하나의 작업만 수행이 가능하기 때문에 기존에 수행하던 작업이 끝나야 그 다음 작업을 수행할 수 있습니다. 그래서 자바스크립트는 현재 실행하고 있는 함수가 있을땐 그 다음의 작업들은 블로킹 작업중단 이 발생합니다.
이렇게 작업이 중단되서 응답없음 상태에 빠지지 않도록 비동기콜백을 활용합니다.
현재 하고 있는 작업이 종료되지 않아도 이후의 작업들을 바로 실행할 수 있는 방식을 비동기처리라고 하는데, 브라우저의 도움을 받아 이벤트 루프가 발생하면서 비동기처리가 가능합니다. 비동기처리를 하면 블로킹이 발생하지 않을 수 있지만 실행 순서가 보장되지 않습니다.
이벤트 루프는 자바스크립트의 동시성을 지원하기 위해 브라우저에 내장되어 있는 기능 중 하나입니다. 자바스크립트 엔진은 단순히 함수가 호출되면 실행 컨텍스트가 콜 스택에 쌓이고 순서대로 처리만 하지만, 이벤트 루프는 자바스크립트 엔진의 콜 스택에 지금 실행하고 있는 실행 컨텍스트의 여부와 테스크 큐에서 나중에 실행할 함수가 대기중인지 반복해서 확인합니다.
번역하면 실행시간이라 하는 만큼, 컴퓨터 프로그래밍이 실행하고 있는 동안의 동작이라는 뜻입니다. 이러한 개념을 확장해서 런타임 환경은 컴퓨터가 실행하는 동안 프로세스나 프로그램을 위한 소프트웨어 서비스를 제공하는 컴퓨터환경을 소프트웨어로 구현한 가상머신 상태를 나타냅니다.
그래서 자바스크립트 런타임은 자바스크립트 코드가 실행되는 동안의 동작을 나타내고, 자바스크립트 런타임환경은 자바스크립트 실행을 위한환경이기 때문에 브라우저와 Node.js가 있습니다.
자바스크립트가 처리가능한 기능이 많아지면서 기존보다 더 빠르게 동작하기 위해 자바스크립트의 코드를 이해하고 실행을 도와주는 프로그램 혹은 인터프리터입니다.
자바스크립트 엔진에는 여러 종류가 있지만 그 중에서 크롬에서 만든 V8엔진, 스파이더몽키, 애플에서 개발한 자바스크립트코어 엔진이 있습니다.
자바스크립트 엔진에는 여러 종류가 있어 일부 차이는 있겠지만, 그 중 V8엔진을 기준으로 하겠습니다.
데이터를 임시로 저장하는 메모리 힙과 메인스레드에서 코드실행의 순서를 보장하기 위한 개념인 콜 스택으로 구성되어 있습니다.
동일한 환경에 있는 코드들을 실행할 때 필요한 환경정보들을 모아 실행 컨텍스트를 활성화하고, 콜 스택에 담습니다. 콜 스택 맨 상단에 실행 컨텍스트가 놓이게 되면 기존에 실행되던 컨텍스트와 관련된 코드의 실행은 일시중단하고 대신 맨 상단에 놓인 컨텍스트와 관련된 코드 예를 들면 함수 내부의 코드 들을 순차적으로 실행하고 실행이 모두 완료되면 콜 스택에서 제거됩니다.
이렇게 실행되기 때문에 가장 먼저 쌓인 전역컨텍스트가 제일 나중에 제거되기 때문에 후입선출 LIFO방식으로 실행하면서 전체 코드의 환경과 순서를 보장합니다.
실행 컨텍스트는 다음에 자세히 알아보겠습니다.
컨텍스트의 한국말로 문맥이나 맥락이라는 뜻으로, 실행 컨텍스트는 자바스크립트 엔진이 코드를 이해하기 위한 자원이라 볼 수 있습니다. 즉, 실행할 코드에 제공할 환경 정보를 모은 객체입니다.
자바스크립트는 실행 컨텍스트가 활성화되었을때 코드 실행이 가능합니다. 따라서 실행 컨텍스트가 활성화되었을 때 식별자가 선언되고, 이 때 선언된 변수를 위로 끌어올리는 호이스팅이 발생, 외부 환경 정보의 정보, this값을 설정하는 this바인딩을 수행하며 실행할 코드에 전달할 환경정보를 모읍니다.
전역공간은 별도의 실행 명령이 없어도 브라우저에서 자동으로 실행되기 때문에 자바스크립트 파일이 열리는 순간 실행 컨텍스트가 활성화 됩니다.
모든 코드는 실행하기 전 평가를 하며 코드를 실행하기 위한 준비를 합니다. 만약 var x = 1;
이라는 코드가 있을때 변수선언을 하는 var x;
를 제일 먼저 실행해서 변수 식별자 x는 스코프에 등록하고 undefined로 초기화되면서 평가과정이 끝납니다.
평가과정이 끝나고 실행과정이 시작되는데, var x;
는 평가과정에서 실행되었기 때문에 x=1;
부분만 실행합니다. 이때 변수 식별자가 스코프에 등록되어있는지 먼저 확인하고 등록되어있다면 값을 할당하고 할당한 결과를 다시 스코프에 등록해 관리합니다.
식별자에 대한 유효범위 입니다. 경계 A의 외부에서 선언된 변수는 A의 외부와 내부 모두 접근이 가능하지만, A의 내부에서 선언된 변수는 A내부에서만 접근이 가능합니다.
ES5까지는 함수에 의해서만 스코프가 생성됩니다.
렉시컬환경의 두번째 수집자료인 외부환경참조 outerEnvironmentReference
에 의해 식별자에 대한 유효범위를 안에서부터 바깥까지 차례대로 검색하는 스코프체인이 가능합니다.
외부환경참조는 현재 호출된 함수가 선언될 당시의 렉시컬환경을 참조합니다.
예를 들어 A함수 내부에 B함수를 선언하고 그 안에 C함수를 선언했을 때, C의 외부환경참조는 B의 렉시컬환경을 참조하고, B의 외부환경참조는 B가 선언되었을때 A의 렉시컬환경을 참조하면서 연결리스트 형태를 띄웁니다.
선언 시점의 렉시컬 환경을 타고 올라가다보면 결국 전역 실행 컨텍스트의 렉시컬 환경이 있을 거고, 전역 컨텍스트의 렉시컬 환경에 있는 외부환경참조는 아무것도 담기지 않습니다. 각 외부환경참조는 오직 자신이 선언된 시점의 렉시컬 환경만 참조하기 때문에 가장 가까운 요소부터 차례대로만 접근이 가능합니다.
이런 구조적인 특성으로 가장 가까운 곳에서 식별자를 발견했을 때 이미 스코프에 등록을 하기 때문에 여러 스코프에서 동일한 식별자를 선언한 경우 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근이 가능합니다. 이렇게 변수에 접근할 수 없는 현상을 변수 은닉화라고 합니다.
코드의 안정성을 위해 전역변수 사용을 최소화하는것이 중요합니다.