개발/프론트엔드 Job Interview

자바스크립트 기본 개념 QnA (feat. 면접 뽀개기)

차얀 2022. 2. 8. 17:02

 

    목차

자바스크립트에서 스레드에 대해 아는데로 말해주세요.

자바스크립트는 싱글 쓰레드 기반으로 동작하는 언어입니다.
그리고 비동기 처리를 위한 이벤트 루프 기반으로 작동합니다.

자바스크립트에는 싱글 스레드 방식으로 함수를 처리하는 콜 스택이 있고,
들어온 비동기 코드들은 Web API 로 인해 콜백 큐로 이동해 대기하고 있다가, 실행 순서가 되면 메인 콜 스택으로 불러와 실행됩니다.
그리고 이 과정이 이벤트 루프에 의해 이루어집니다.

이런 과정을 통해 비동기 코드를 처리해 동시에 코드가 돌아가는것처럼 실행됩니다.


추가)
- 멀티 쓰레드 방식으로 작동한다면 동시성 문제를 해결해야하고, 이를 위한 복잡한 시나리오들을 신경써야 합니다.

 

 

일급 객체의 개념을 자바스크립트와 관련지어 말해주세요.

아래 3가지 조건을 만족하는 객체를 의미합니다.

1. 변수에 할당 가능
2. 인자로서 전달 가능
3. 함수의 결과로서 반환(return) 가능

자바스크립트에선 함수가 이에 해당됩니다.

 

 

명령형 프로그래밍 방식과 선언형 프로그래밍의 방식에 대해 이야기 해주세요.

문제를 해결하는 과정에서 어느 부분에 집중하는지에 따라 차이가 있습니다.

명령형은 무엇을 "어떻게" 해결하는지에 집중화고, 선언형은 "무엇을" 할것인지에 집중합니다.
대부분의 선언적 접근 방식은 일종의 명령적 추상화가 존재합니다. (ex. map, filter 등)

 

 

 

자바스크립트에서 불변성을 유지하는게 중요한 이유가 무엇인가요?

불변성을 유지함으로서 "예측 가능성"과 "성능 효율성"에서 이득을 볼 수 있습니다.

예측 가능성은 특정 변수의 값이 변경됨을 예측할 수 있음으로서 그에 따른 동작을 예상할 수 있습니다.
만약 불변성이 유지되지 않는다면, 특정 값을 참조하고 있는 변수들중 하나의 변수에서 값을 변경한다면,
다른 변수에선 그 값이 변경되었는지 여부를 바로 파악하지 못하는 side effect가 발생하게 됩니다.

성능 효율성은 크게 두가지로 나뉩니다.
우선 메모리 영역의 성능으로, 동일한 값을 가지고 있는 여러변수들에 대해 동일한 값 메모리 영역을 참조함으로서 성능 개선.
두번째로 상태 변경 추적 성능으로서, 특정 변수의 값이 변경되었는지 여부를 파악하기 위해 매핑된 주소값만 참조하면 됩니다.

 

 

 

자바스크립트에서 모듈이 무엇이고 이를 사용하는 이유는 무엇인가요?

전역 상태 오염을 막기 위한 캡슐화를 위해 사용한다.
(최상위 스코프에서 var로 변수 선언시 전역으로 들어가게 되어,
모르고 서로 다른 영역에서 동일한 변수명을 사용해 side effect가 발생할 수 있다
모듈은 이런 전역이 아닌 모듈 스코프 영역에 변수를 선언하게해, 이런 side effect를 예방한다.)

모듈 안의 모든 기능은 모듈 안에서만 동작하며, 밖에서는 접근이 허용된 속성이나 메서드만 사용 가능하다.
ES5까지는 모듈에 대한 지원이 되지 않았기에, 모듈과 비슷한 구조를 즉시 실행 함수(IIFE)를 이용해 구현했다.

[모듈의 종류]
1. ES2015
  : 동기식 및 비동기식 로딩 모두 지원
  : 모든 브라우저가 지원하지않아 babel 사용
2. CommonJS 
  : ES2015 이전 JS에 없던 모듈 시스템 구현 방법
  : 서버사이드 및 데스크탑 어플리케이션에서 지원, NodeJS에서 사용
  : require, module.exports 사용
3. AMD
  : ES2015 이전 JS에 없던 모듈 시스템 구현 방법
  : 비동기 모듈 표준안
4. UMD
  : CommonJS + AMD 패턴

 

 

 

실행 컨텍스트에 대해 말해주세요.

실행할 코드에 제공할 환경 정보를 모아놓은 객체입니다.
대표적으로 호이스팅이 일어나게 하는 환경 레코드(enviroment record),
클로저가 일어나게 하는 외부 렉시컬 참조(outer LexicalEnvironment Reference),
this 정보(this Binding)등의 정보를 포함하고 있습니다.

'전역영역', 'eval함수로 실행된 경우', '함수를 생성한 경우' 이렇게 3가지의 경우에만 실행 컨텍스트가 생성됩니다.
더보기

[실행 컨텍스트 개념 전체적으로 보기]

실행 컨텍스트는 자바스크립트의 스코프, 호이스팅, 클로저와 같은 주요 개념을 설명하기 위한 핵심 원리입니다.

실행컨테스트가 생성되는 조건은 '전역영역', 'eval함수로 실행한경우', '함수를 생성한경우' 이렇게 3가지입니다.

그리고 위 조건으로 실행컨텍스트가 생성되면, 콜 스택에 쌓이는 방식으로 작동합니다.

 

 

[실행 컨텍스트의 구성]

- code evaluation state : 실행 컨텍스트의 코드를 실행, 일시 중단, 재개하는데 필요한 모든 state 정보

- function: 실행 컨텍스트가 함수코드 평가하는 경우 이 값은 해당 함수의 객체. 그외 평가 시엔 null

- Realm : ECMAscript 리소스에 접근하는 코드에 대한 렐름 레코드

- ScriptOrModule: 모듈, 스크립트 레코드. 둘다없는 경우엔 null

- VariableEnvironment

- LexcialEnvironment

 

 

[VariableEnvironment와 LexicalEnvironment]

우선 렉시컬환경과 변수환경둘다 동일하게

environment record(환경레코드), outerLexicalEnvironment Reference(외부 렉시컬 참조) 로 구성되어있습니다.

 

우선 환경 레코드는 내부 컨텍스트의 식별자와 그의 바인딩값을 기록합니다.

이때 'var, 함수 선언문' 과 'let, const, 함수 표현식'의 정보가 서로 다른 방식으로 수집됩니다.

('var,함수선언문'은 Variable의 환경 레코드에, 'let,const,함수표현식'은 Lexical에 저장된다)

 

우선 수집되는 정보들은 전부 해당 컨텍스트 최상단으로 수집되게 됩니다(즉, 전부 호이스팅 발생).

 

다시돌아와, outerLexicalEnvironment Reference(외부 렉시컬 참조)는

외부 실행 컨텍스트를 가르킵니다.

이때, 현재 lexcial은 현재 variable가르키고, 현재 variable은 외부 lexical을 가리킵니다.

 

그래서 검색하고자 하는 식별자가 없는 경우에 이를 이용해 외부 렉시컬 환경을 찾고 이를 전역까지 검색하고,

이게 바로 스코프 체이닝의 개념이자 클로저가 발생하는 이유입니다.

 

 

[this 정보의 위치 변경]

this에 대한 정보도 환경레코드에서 가지고 있습니다.

원래 es5에서는 실행컨텍스트에서 가지고 있던 정보였지만,

es6로 넘어오면서 환경레코드가 가지고 있게끔 변경되었습니다.

 

 

[함수 스코프와 블록 스코프]

새로운 함수가 생성되면 새로운 실행컨텍스트가 생성됨과 동시에 새로운 함수 스코프도 생성됩니다.

하지만 블록 스코프의 경우엔 아까 실행컨텍스트가 생성되는 조건에서는 블록이 존재하지 않았습니다.

 

즉 블록은 생성될때, 새로운 실행컨텍스트를 생성하지 않고, 현 실행 컨텍스트 렉시컬환경에

새로운 렉시컬환경을 별도로 생성해 새로 생성한 렉시컬환경을 바라보도록했다가 종료시킵니다.

 

 

 

호이스팅에 대해 말해주세요.

함수 내 선언들을 해당 함수 스코프의 최상단으로 끌어올려서 선언되는 것을 의미합니다.

console.log(a);   // undefined
var a = 1;

이때 var은 수집과 동시에 초기화가 일어나고, 함수 선언문은 말그대로 선언문이기에 최상단으로 이동하게되면
우리가 흔히 아는 호이스팅의 방식으로 작동하게 됩니다.
 
반면 let, const, 함수표현식은 초기화는 발생하지 않고 선언 만되기에 TDZ(일시적데드존)이 발생하게 됩니다.

 

 

스코프란 무엇일까요?

변수에 접근 가능한 범위를 의미합니다.

기본적으로 함수 레벨 스코프 방식으로 작동하고, ES6의 let과 const 부턴 블록 레벨 스코프 방식으로 작동합니다.
이때, 스코프는 스코프 체이닝 방식으로 상위 스코프를 검색하는 방식으로 작동하게 됩니다.
이에 따라, 특정 변수의 값은 해당 스코프의 호출 위치가 아닌 선언 위치가 중요합니다.

 

 

 

클로저란 무엇일까요?

어떤 함수를 호출했을때, 그 함수의 외부환경의 정보를 사용할 수 있는 개념입니다.

실행 컨텍스트의 outerLexicalEnvironment Reference(외부 렉시컬 참조) 정보를 통해 접근이 가능합니다.
마찬가지로, 스코프 체이닝 방식으로 작동합니다.

보통, 모듈 패턴을 통한 데이터 프라이버시를 위해 사용합니다.

 

 

 

this 바인딩에 대해 아는데로 말해주세요.

함수의 형태 및 호출 방식에 따라 this의 정보가 달라집니다.

1. 화살표 함수 호출
  화살표 함수는 자신만의 this를 가지지 못합니다. (constructable 하지 않기 때문)
  그래서 외부 스코프를 체이닝해나가며, 가장 가까운곳에서 가지고 있는 this의 값을 계승받는다. (= Lexical This)
  물론 call, apply, bind와 같은 방식으로 this를 지정해주지도 못합니다.

2. new를 이용해 일반 함수 호출
  해당 함수의 정보를 가지고 있을 새로운 객체를 생성합니다. (constructable한 일반 함수)

3. 명시적 바인딩(call, apply, bind)를 이용한 일반 함수 호출
  this는 인수로 전달된 객체입니다.

4. 암시적 바인딩을 이용한 일반 함수 호출
  obj.method()와 같이 함수인 프로퍼티를 호출하는 경우를 의미합니다.
  이때 this는 해당 객체를 의미합니다 (즉, obj를 의미합니다)

5. 기본 바인딩
  일반적으로 함수를 호출하는 경우, this는 전역 객체(브라우저에선 window, 노드에선 export)를 의미합니다.
  이때 엄격 모드의 경우엔 undefined를 가르킵니다.

 

 

화살표 함수와 일반 함수의 차이점에 대해 말해주세요.

우선 일반함수는 constructable + callable 하지만, 화살표함수는 callable하되, constructable하지 못합니다.
(추가로 class는 constructable하되 callable 하지는 않습니다)

이로 인해, 화살표함수는
1. this에서 가지고 있는 정보가 다릅니다.
   (일반 함수는 호출방식에 따라 this 정보가 동적으로 결정되지만, 화살표 함수는 언제나 상위 스코프의 this를 가르킴)

2. 생성자 함수(new)로 사용이 불가능합니다.
   constructable하지 않기때문에 prototype 프로퍼티를 가지고 있지 않기 때문입니다.
   이에 마찬가지로, prototype내 메서드를 사용할 수 없습니다.

3. arguments 인자를 사용 불가능합니다.

 

 

 

프로미스에 대해 말해주세요.

자바스크립트에서 비동기를 처리하는 방법중 하나로,
콜백의 단점(콜백 헬)을 해결하려는 시도에서 만들어졌습니다.

3가지 상태 (pending(new Promise() 호출 시점, fulfilled, rjected)가 존재합니다
주요 특징으로, 딱 한번의 성공 혹은 실패만을 합니다.
then을 이용한 체이닝으로 처리할 수 있고, 마지막에 catch 메서드를 통해 에러 핸들링을 할 수 있습니다.

[관련 메서드]
1. Promise.all()
  : 순회가능한 객체에 주어진 모든 프라미스를 실행해, 그 결과값을 배열 형태로 내보냅니다.
  : 단, 이중 하나라도 실패시, 해당 실패 reject를 반환하며 실행이 중지됩니다,

2. Promise.race()
  : 순회가능한 객체에 주어진 모든 프라미스를 실행해, 가장먼저 실행결과를 내보내는 프라미스를 반환합니다.
  : 이때 그 결과의 resolve, reject 여부는 상관없습니다.

3. Promise.allSettled()
  : 순회가능한 객체에 주어진 모든 프라미스를 실행해, 그 결과값으 배열 형태로 내보냅니다.
  : 이때 resolve, reject 여부와 상관없이 전부 실행해 그 결과값을 무조건 배열형태로 반환합니다.

4. Promise.any()
  : 순회가능한 객체에 주어진 모든 프라미스를 실행해, 가장먼저 resolve되는 프라미스를 반환합니다.
    : 보통 동일하지만 서로다른 환경에 있는 서버에 요청을 보내 가장빠른 응답값을 가져오기 위해 사용됩니다.


[프라미스의 한계]
1. 프라미스 체이닝에서 에러 발생시, 중간 체인에서 자체적 에러처리기가 있으면, 최하단의 catch가 호출되지 않습니다.
2. resolve되는 값은 단일 값이기에, 여러 값을 전달하고 싶으면 자체적으로 객체나 배열로 감싸서 전달해야 합니다.

 

 

 

Async, Await에 대해 말해주세요.

자바스크립트 비동기 처리 방식중 하나로, 콜백함수와 프로미스의 단점을 보안한 시맨틱 슈거 역할을 수행합니다.

[async/await의 장점]
1. 간결함과 깔끔함
  : 프라미스와 달리, async/await 를 사용해 보다 간단하고 친근한 형태로 비동기를 구현할 수 있습니다.
   (.then 필요없고, response 해결위한 비동기 콜백 함수 생성 필요없음)

2. 에러 핸들링
  : async/await를 이용한 비동기 코드 뿐만 아니라 동기 코드에 대한 에러를 모두 하나의 try/catch문을 통해 처리가능합니다.
   (기존 프라미스에선 .catch를 이용해 비동기 코드에 대한 에러처리를 수행하기 때문에, 에러처리 코드의 중복가능성) 

 

 

 

이벤트 루프 작동방식에 대해 말해주세요.

이벤트 루프는 단일 스레드 방식인 자바스크립트에서 비동기코드를 동작하게 해주는 역할을 수행합니다. (동시성 처리역할)

코드가 실행되면 콜스택에 코드들이 쌓이기 시작합니다.
이 콜 스택은 순서대로 코드를 실행하는 단일 스레드 방식으로 동작합니다.
이때 비동기 코드가 콜스택에 들어오게 되면 Web API가 호출되어, 비동기 함수의 콜백함수를 콜백 큐에 넣습니다.

그리고 이벤트 루프는 콜백이 빈 상태가 되면
콜백 큐에 있는 실행 준비 상태에 있는 콜백함수를 콜스택으로 이동 시킵니다. (tick(틱)의 개념)


[콜백 큐(테스크 큐)]
크게 microtask Queue(마이크로 큐)와 macro(매크로 큐)로 2가지로 나뉩니다.

매크로 큐에는 I/O, requestAnimationFrame, UI 렌더링, setTimeout, setInterval 과 같은 콜백들이 들어가고,
마이크로 큐에는 Promise, process,nextTick, queueMicrotask와 같은 콜백들이 들어가게 됩니다.

한번의 틱에서 매크로 테스크 큐에서 테스크를 하나 꺼내 콜스택으로 보내 실행시킵니다.
그리고 이어서 마이크로 테스트 큐에 있는 모든 테스크를 콜스택으로 보내 실행시킵니다.
(이때 마이크로 테스트 큐가 새로운 마이크로 테스트 큐를 추가시킬수 있고, 이때 추가된 것들도 빌때까지 계속 실행됩니다)
이게 반복되어 실행됩니다.

 

 

 

이벤트 버블링, 캡처링이란 무엇이고 이를 사용하는 케이스에 대해 말해주세요.

특정 요소에 대한 이벤트가 전파되는 것을 의미합니다.
버블링은 특정 요소의 부모 요소로, 캡처링은 특정 요소의 자식 요소로 전파되는 특징을 가지고 있습니다.

버블링의 개념을 이용해 이벤트 위임을 구현할 수 있습니다.
event.propagation()을 이용해 부모 요소로의 버블링을 방지해줍니다.
event.stopImmediatePropagation()를 이용해 부모 요소의 버블링을 방지할 뿐더러, 할당되어있는 다른 핸들러의 동작도 방지해줍니다.

캡처링은 일반적으로 사용되는 개념은 아닙니다.
보통 addEventlistener의 2번째인자를 true로 설정해줌으로써 캡처링을 적용할 수 있습니다.
굳이 활용하자면 특정요소의 하위요소에 focus 및 blur를 주기 위해 사용되는 경우가 있습니다.

 

 

 

이벤트 위임에 대해 말해주세요.

상위 요소 하나에 이벤트를 달아 하위 요소 이벤트를 대체하는 방법입니다.

1. 메모리 사용 공간 감소 (하나의 이벤트 리스너만 작성하면 된다)
2. 제거된 요소에서 핸들러 해제하고 다시 바인딩해주는 불편함 감소
의 효과가 있습니다.

참고로 리액트에서든 이벤트들을 루트딴에서 전부 수집해 실행하기때문에 이런 이벤트 위임의 효과를 기대하기 어렵습니다.

 

 

프로토타입에 대해 말해주세요.

자바스크립트는 프로토타입 기반 언어입니다. (상반. 클래스 기반 언어)

프로토타입 기반 언어는 특정 객체를 원형(프로토타입)으로 삼고
이를 복제(참조)함으로써 상속과 비슷한 효과를 얻습니다.
(반대로 클래스 기반언어는 상속을 사용하고, ES6에서 클래스 문법이 생겼다해도 JS가 클래스 기반으로 바뀐건 아닙니다.)

즉, 자바스크립트의 모든 constructalbe 객체에서는 prototype을 가집니다.
이 prototype에는 원형 객체(상위 스코프)의 정보를 가지고 있고, 현재 스코프내에 해당 프로퍼티가 없으면
prototype에서 프로퍼티를 찾습니다. ( = 프로토타입 상속)

이때, prototype의 정보는 [[prototype]]의 내부 매서드 형태로 직접 접근이 불가능하기에,
__proto__ 프로퍼티를 통해 간접적으로 접근이 가능하다.
(내부 슬롯과 내부 메서드는 JS 엔진의 구현 알고리즘을 설명하기 위해
ECMAScript에서 사용하는 의사 프로퍼티로 직접 접근이 불가능합니다)

 

 

call, apply, bind의 차이에 대해 말해주세요.

우선 셋다 this에 대한 명시적 바인딩을 수행하는 방법입니다.

call과 apply의 차이는 바인딩한 this 이후 2번째 인자부터 전달되는 매개변수를 나열하는 방법의 차이가 있습니다.
call은 매개변수를 2, 3...n번째 인자로 순서대로 나열함에 반해, apply는 두번째 인자에 배열로 받아 나열합니다.

다음으로, bind는 call과 apply는 작성한 함수를 즉시 호출함에 비해,
bind는 변수에 할당해 원할때 호출하는 방식입니다. 
(이때 bind의 변수 나열방식은 call과 동일)

 

자바스크립트의 원시 타입의 종류와 개수에 대해 말해주세요.

string, number, boolean, undefined, null, symbol, bigint 로 총 7개입니다.

Symbol은 동일한 string이라해도, symbol타입으로 선언하게되면 고유한 값으로 취급됩니다.

 

 

 

엄격 모드(strict mode)란 무엇이고 이를 사용하는 이유는 무엇인가요?

자바스크립트의 암묵적인 느슨한 모드(sloppy mode)를 해제하기 위한 방법으로,
기존에 조용히 무시되던 에러들을 throw 합니다.
(8진법 문법 사용 방지, 변수명을 delete, eval, argument, implements, let, pulic등 사용 방지등)

보통 코드의 잠재적인 문제를 방지하기 위해 사용됩니다.
ES5부터 사용되기 시작했고, 'use strict' 구문을 적용하고자하는 구문 작성 전에 삽입하는 방식으로 구현합니다.
ES6에서부턴 type="module"로 사용되는 파일들에 대해 strict mode가 자동으로 적용됩니다. 

 

 

 

Object, Map, Set의 차이점에 대해 말해주세요.

Map과 Set은 ES6에서부터 새로 도입된 자료구조 입니다.

Object와 달리 Map과 Set은
1. key값으로 문자열/심볼외의 값을 사용할 수 있습니다.
2. 객체의 프로퍼티 개수를 알기위한 메서드가 존재합니다 (size)
3. 객체를 for of 문으로 쉽게 순회 가능한 방법이존재합니다 (Map: entries 메서드, Set: spread 문법)
4. 프로퍼티의 순서를 보장합니다.

Map은 키-값 형태로 Object와 유사하게 저장되고,
Set은 객체에 값이 저장되는 형태로 저장되고 중복된 값이 존재하지 않습니다.



[WeakMap]
get, set, delete, has 프로퍼티만 사용가능합니다 (keys, values, entries 미지원)
key가 반드시 객체여야 합니다 (원시값이 key가 될 수 없습니다)
key값으로 사용되는 객체를 참조하는 곳이 없으면, 해당 객체는 메모리와 Weakmap에서 자동으로 삭제됩니다.
보통 특정 객체 데이터에 추가적인 데이터를 저장할 필요가 있을때 유용합니다.
또, 특정 함수의 파라미터 obj일때 결과값을 저장하기 위한 캐싱에도 유용합니다.


[WeakSet]
add, has, delete 프로퍼티만 사용가능합니다 (keys, size 미지원)
객체만 저장할 수 있습니다.
마찬가지로, 저장하고 있는 객체를 참조하는 곳이 없으면, 해당 객체는 메모리와 WeakSet에서 자동으로 삭제됩니다.
보통 저장된 객체들에 대한 Boolean 과 같은 결과를 얻는 용도로 사용됩니다.