티스토리 뷰

카테고리 없음

Javascript 면접 문제 답변

지우 초이 2021. 6. 3. 01:58

1. 이벤트 루프란?

Javascript 엔진은 싱글스레드를 기반으로 만들어졌습니다. Callstack에는 Javascript에서 내린 명령들이 한개씩 쌓이고, 그것들을 싱글스레드 환경에서 한개씩 해결합니다.

 

그렇다면 비동기 프로그래밍은 어떻게 할 수 있을까요?

 

Javascript엔진은 싱글 스레드(Memory Heap + CallStack) 지만, 기본적으로 그 엔진을 돌리는 브라우저/혹은 node.js 런타임 환경은 싱글 스레드 환경이 아닙니다. 

 

브라우저를 기초해 설명하자면,

(1) Javascript에서 Web Apis를 부릅니다.

(2) 별도의 쓰레드에서 Web APIs (Timeout, DOM, AJAX) 들이 작동합니다. 

(3) Web API들의 결과로 사용되는 Callback들은 차례대로 Callback Queue에 적재됩니다.

(4) CallStack이 빌 때 까지 기다리다가, CallStack이 비면 Callback Queue에서 콜백을 하나씩 callstack에 추가해줍니다.

(5) 이 때, callstack이 들어갈때까지 체크해주는것을 이벤트 루프가 담당합니다.

 

이벤트 루프 덕분에 event-driven programming이 가능 합니다.

 

이 과정은 사실 렌더링과도 밀접한 관련이 있습니다.

브라우저는 렌더링을 60fps로 맞추려고 노력합니다.

하지만 Javascript때문에 이 60fps가 깨지기도 합니다.

콜스택에 어떤 값이 잔재해 있다면, Render를 하지 못합니다. 마치 callback큐에 들어있는 callback처럼 운용됩니다.

하지만 좀 다른점은, 더 높은 우선순위를 가지고 있기 때문에 그래도 최대한 60fps를 맞출 수 있습니다.

 

하지만 콜스택에 느린 동기적 코드가 담겨있다면, 확실히 랜더링 속도에 영향을 미칠 수 있습니다.

이런 부분을 체크해줄 필요가 있습니다.

 

이벤트루프를 조금 더 심화해보면, Microtask Queue(ES6) /Animation Frame (requestAnimationFrame) 등 다양한 목적에 맞는 큐도 존재합니다.

여러 우선순위를 조정해서, 랜더링이나, 작업 처리에 효율을 가하려는것을 알 수 있습니다.

 

https://velog.io/@thms200/Event-Loop-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84

https://iamsjy17.github.io/javascript/2019/07/20/how-to-works-js.html

 

2. JS 엔진 관련 질문들

- 실행 컨텍스트에 대해서 설명하시오

- 자바스크립트 호이스팅은 어떻게 이루어지는가

- 클로저란 무엇이며, 왜 이러한 패턴을 사용하는가?

 

위와 같은 질문들은 단골 질문이기도 하지만, 자바스크립트에 대한 이해를 얼마나 하고 있는지 알 수 있는 좋은 질문이기도 합니다.

위 내용들이 모두 연관되어있는 질문이기 때문에 한번에 정리해보겠습니다.

 

2-1. ES3 와 ES5의 차이

ES3와 ES5에는 근본적으로 몇가지 차이가 발생하는데, 그 중 한가지가 바로 실행 컨텍스트이다.

ES3의 주된 엔진 키워드는 "스코프 체인-식별자 해결" 관련이고

ES5의 주된 엔진 키워드는 "실행 환경-식별자해결", "렉시컬 환경" 이다.

 

ES5는 어떤 함수를 실행할 때, 실행 환경을 다 설정하여서 엔진이 사용한다고 보면 되고,

Execution Context = 함수가 실행될 때, 어떤 묶음으로 (어떤 단위로) Context를 가져갈 것인가?

= Execution : 해석된 실행 환경을 바탕으로 코드를 실행하는것.

= Context : 함수에서 사용할 환경등을 모두 담아두었다.

 

실행환경을 가져갈까?

=> 식별자 해결을 위해서.

어떤 스코프 내에서 식별자들을 해석하기 위한 재료들을 context에 넣어두고, 실행할 때 그것을 참조한다.

대표적으로는 this나, 외부 스코프 참조 등이 있다.

 

실행환경을 통해서 엔진 입장에서는 손쉽게 관련 정보들을 가져갈 수 있다.

 

ES3는 식별자 해결을 위해서 스코프체인(Scope Chain) 을 활용한다. 스코프체인은 식별자 해결을 위해서 다른 어딘가의 엔진에서 스코프를 계속 찾아나간다는 관점이다. 함수가 실행되면 scope를 생성하고, 함수와 변수등을 리스트등으로 정해두어서 식별자를 해결하는 방법이다. 이는 정적(Lexical Enviroment) 으로 실행환경(Execution Context)을 만들어 놓고 그 안에서 모든걸 해결(ID Resolving) 하는 "ES5"의 JS 엔진과는 사뭇 다른모습이고, 그만큼 엔진의 연산에 추가적인 코스트를 부과할 수 밖에 없다.

 

정리하자면, 실행환경에 필요한 하나의 묶음으로 가져가는게 ES5, 그것보다 더 작은 단위로 묶어서 찾아 나가는것을 ES3라 볼 수 있다.

 

2-2.  실행 컨텍스트 구조.

인프런, 김영보님 JS 중고급 강의

1) book()을 실행하면, 엔진 컨트롤이 함수 안으로 이동한다.

2) 함수 부분이나, 선언 부분들을 엔진 순서에 맞게 읽어 들인다.

3) show()라는 함수를 만나게 되면, 함수이므로 함수 Object를 생성한다.

4) 이 때, show function object에 [[scope]] (엔진이 사용하는 부분)를 설정한다. => 함수 내부에서 외부의 값을 사용할 수 있는 이유이다. (context로 묶이기 때문)

5) show()함수를 호출한다.

6) 엔진 컨트롤이 show()함수 내부로 들어간다. 들어가기전에 실행 컨텍스트를 만든다.

7)

- 렉시컬 환경 컴포넌트 : title같은 내부 변수나 함수를 기록해둔다 (DER). [[scope]]에 설정된 외부 렉시컬 환경을 "외부 렉시컬 환경 참조"에 넣는다.  (ODER)

- this 바인딩 컴포넌트 : this로 참조할 오브젝트를 여기에 설정한다. 여기서는 아무런 object도 작성하지 않았기 떄문에 글로벌 오브젝트인 window가 바인딩 된다.

 

function object를 만들었을 때, [[scope]]가 결정된다. 이것을 정적 렉시컬 스코프라고 합니다.

 

2-3. 정적환경

function키워드를 만나면, function obj를 생성하고, 스코프(함수 밖의 스코프) 를 function obj의 [[scope]]에 설정(결정)한다.

이 시점에서 스코프가 결정되기 때문에, 이를 정적 스코프라고 한다. (코드가 쓰여진 그 위치대로 스코프를 판단)

 

함수가 호출되면, 엔진 컨트롤이 함수 내부로 들어가면서 실행 환경을 꾸린다.

이때, [[scope]]를 외부 렉시컬 환경 참조에 설정한다.

 

2-4. use strict

ES5관련된 엔진 방식을 따라가다 보면, 한가지 이해가 안되는 부분이 있는데 바로 스코프 부분이다.

언제든지 자유롭게 글로벌 스코프에 해당하는 변수를 선언할 수 있는 상황이나, 렉시컬 환경 스코프를 제대로 생성하지 못하게 하는 요소들 (this 바인딩) 같은 경우를 방지하기 위해서, es5에서는 use strict를 활용한다.

 

ES6같은경우는 let, const같은 블록 레벨 스코프에 할당되는 스코프를 할당하여서, 문제를 해결한다.

 

2-5. 호이스팅

자바스크립트 엔진이 해석하는 순서는 총 3가지입니다.

 

1. 함수키워드를 읽어서 함수 오브젝트로 변환한다.

2. 변수 초기화.

3. 코드 실행.

 

인프런 김영보님, JS 중고급

따라서 변수값들이 맨 위로 움직이는 현상처럼 보일 수 있으나, 엔진 실행 관점에서 보면

1. function을 먼저 읽기 때문에 getBook에 대한 스코프를 해석하고

2. 그다음에는 title같은 변수를 해석하고

3. "JS책" , function() {} 같은 것들을 주입시킨다.

 

따라서 함수 앞에서도 함수를 호출할 수 있는데, 이를 호이스팅이라고 한다. 이것에 대한 논리가 위와 같다.

2-6. Function Object

자바스크립트에서 Function을 만나면, 그 Function을 가지고 Function Object를 만든다고 했다.

prototype에 연결된 메소드로 function 오브젝트를 생성한다.

즉, 우리가 알고 있는 function의 모습은 { } 이런 오브젝트의 형태로 엔진에서 변환하여 해석한다.

이런 오브젝트에 들어가는 값들은

1. prototype : 빌트인으로 들어가있는 Object에 대한 속성들이 들어가는 부분. 인스턴스화가 되면 그 인스턴스는 자연스럽게 이 속성을 참조하여 사용합니다. 그렇지 않으면 아래의 __proto__ (function)용을 사용합니다. 또 헷갈리게 이 prototype에도 아래 __proto__와 같은  __proto__ 속성이 존재하는데요. 이는 Object를 위한 Object.prototype 값들이 들어가있으며, 이렇게 엔진이 쓸 영역들을 구분해둡니다.

 

 

2. __proto__ : 여기는 Function에 관련된 prototype 속성을 가지는 곳입니다. 예를들어 call(), bind(), apply()등이 

3. 각종 프로퍼티들 (arguments, caller, length 등..)

 

 

2-6. 클로저

클로저는 함수의 외부의 값들을 참조할 수 있는것을 의미.

JS에서는 function obj를 생성할 때 [[scope]]를 생성한다고 했는데, 그 값들을 사용하는것이 단순 클로저의 논리다.

 

어떤 블럭 내부는 보통 외부에서 직접 접근이 불가능하므로, 정보 보호할 수 있으며, 클로저 논리를 통해 외부에도 접근 가능하기 때문에

재사용과 정보보호 두가지를 다 취할 수 있다.

 

3. ES6 관련 질문들

3-1. Arrow Function

앞서 말했던 function object의 특징인 prototype과 constructor가 없다.

즉, 생성자 함수로써 생성(new 연산 못함), 확장 은 못하고 단순히 함수 실행기로써 존재할 수 있다.

그만큼 더 가볍다는 장점이 있다.

 

또 this를 해석하는게 다른데, [[scope]]에서 설정된 this가 화살표 내부에 자동으로 바인딩 되어 활용할 수 있다.

 

3-2. let, const 

- let : 변경할 수 있다.

- const : 변경할 수 없다.

- 변수가 선언된 블럭이 스코프가 된다.

- var를 글로벌 스코프에 적으면 글로벌 변수가 되지만, let은 그렇지 않다.

- let은 또한 호이스팅 되지 않는다. 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함