프론트엔드 면접 핸드북 - 자바스크립트(2)
이 인터뷰 핸드북은 front-end-interview-handbook/javascript-questions을 기반으로 정리한 것입니다.
21. 이벤트 버블링에 대해서 설명하라
한줄 답변: DOM 이벤트가 상위 요소(element)로 전파되는 현상
DOM 요소에서 이벤트가 발생했을 때, 거기에 등록된 이벤트 리스너 함수가 있으면 실행을 시도한다. 그리고 이벤트는 부모 요소를 통해 DOM 트리 위쪽으로 전파(bubbled up)되며 거쳐가는 DOM 요소마다 같은 일(이벤트 리스너 확인 및 실행)이 반복된다. 이 버블링은 최상위 요소인 document
까지 진행된다. 이벤트 버블링은 이벤트 위임(delegation) 메카니즘 안에 있다.
22. attribute와 property의 차이점이 무엇인가?
한줄 답변: attribute도 DOM property 중 하나다
attribute는 HTML 마크업에 정의되어 있지만 property는 DOM 객체에 정의되어 있다. 차이점을 설명하기 위해, HTML 안에 다음의 텍스트 필드가 있다고 가정해보자
<input type="text" value="Hello" />
DOM 트리에 HTMLInputElement
객체가 생성된다. 이 객체는 수많은 property를 가지고 있다. (accept, accessKey, align, alt, attributes, autofocus, baseURI, checked, childElementCount, childNodes, children, classList, className, clientHeight, etc.) attribute는 DOM 객체의 attributes
property가 가진 요소들이다.
DOM node가 생성되면 많은 수의 property가 attributes에 있는 요소와 같거나, 비슷한 이름을 가지고 있다. 하지만 모두 1대1 대응을 하지는 않는다.
<input id="the-input" type="text" value="Name:" />
id property는 id attribute 값을 반영한다. id property를 바꾸면 id attribute도 변경을 반영한다.
하지만, value
property는 그렇지 않다. value는 input 엘레멘트의 현재 입력값을 반영한다. 사용자가 입력하면 value property는 바뀌지만, value attribute는 초기값을 그대로 유지한다. 만약 사용자가 input 필드가 John을 입력했다면 아래와 같은 결과를 얻을 수 있다.
var theInput = document.getElementById("the-input");
theInput.value; // returns "John"
theInput.getAttribute("value"); // returns "Name:"
value property는 바뀌었지만 value attribute는 초기값인 Name:
그대로다. attribute와 property의 관계는 몇 가지로 나뉠 수 있다.
-
attribute를 바로 반영하고 이름까지 같은
rel
,id
등
-
attribute 변경 반영하지만 이름이 조금 다른
htmlFor
(property) -for
(attribute) ,className
-class
등
-
attribute 값을 반영하지만 수정에는 영향이 없는
src
,href
,disabled
,multiple
등
관련 상세 스펙은 아래 링크에서 확인할 수 있다.
https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflect
관련 자료
23. Javascript 내장 객체를 확장하는 것이 왜 좋은 방법이 아닌가?
한줄 답변: 나만 확장한 것이 아니라 다른 사람도 확장했다면? 충돌은?
내장(built-in) 객체를 확장하는 것은 property/function을 prototype에 추가한다는 말이다. 처음에는 좋은 아이디어처럼 보일 지 몰라도, 실제로는 위험한 방법이다. 당신이 코드에서 2개의 라이브러리를 사용하는데, 둘 다 Array.prototype을 확장해 contain
이라는 메소드를 추가해서 구현한 것이라고 가정해 보자. 두 라이브러리를 모두 불러오면, 마지막에 불러온 코드가 다른 라이브러리의 contain
메소드를 덮어쓰게 된다. 두 라이브러리가 contain
을 같은 결과를 만들도록 구현했다면 문제가 없겠지만, 아니라면 어느 한 라이브러리는 문제를 일으킬 것이다.
기본 객체를 확장해야 하는 경우는 대체 코드(polyfill)을 만들어야 할 때 뿐이다. 예를 들어 IE 브라우저가 지원하지 않는 Array.prototype.includes
를 사용하려 할때, Array.prototype
에 includes
메소드를 추가해야 할 필요가 생긴다.
관련 자료
24. document의 load 이벤트와 DOMContentLoaded의 차이점은?
한줄 답변: 실행 시점이 페이지에 포함된 리소스가 모두 로딩된 후인지 아닌지의 차이
DOMContentLoaded
이벤트는 HTML 도큐멘트가 최초로 로딩되고 파싱되었을 때 발생한다. 스타일시트, 이미지, 서브프레임의 완전한 로딩은 기다리지 않는다. 그에 비해 window
의 load
이벤트는 DOM과 페이지가 포함한 모든 리소스가 로딩되었을 때 발생한다.
25. === 과 == 연산자의 차이는?
한줄 답변: loose, strict equality
==
추상적인 등가 연산자, ===
는 엄격한(strict) 등가 연산자다.
==
는 필요하다면 타입 변환을 하면서 비교를 하고, ===
는 있는 그대로 비교한다. 그래서 몇몇 케이스에서 두 연산자가 리턴하는 값이 다를 수 있다. ==
는 loose equality 연산자라고도 한다.
1 == "1"; // true
1 == [1]; // true
1 == true; // true
0 == ""; // true
0 == "0"; // true
0 == false; // true
==
는 사용하지 말아야 한다. 예외가 있다면 null
과 undefined
를 비교할 때는 편리한 측면도 있다. 물론 두 값이 가진 의미는 다르다. null
은 명시적으로 이 값이 없음을, undefined
는 변수에 아직 값이 할당되지 않았음을 의미한다.
관련 자료
26. 자바스크립트와 관련한 same-origin 정책에 대해 설명
한줄 답변: 다른 도메인의 스크립트를 사용한 해킹 방지
동일 출처 원칙(same-origin policy)는 자바스크립트가 서로 다른 도메인 사이에 리퀘스트를 날리는 것을 방지한다. origin은 URI 스키마, 호스트네임, 그리고 포트 번호로 정의된다. 이 정책은 다른 도메인의 악의적인 스크립트가 타겟 웹페이지에서 DOM을 통해 민감한 정보를 가져가는 것을 방지한다.
관련 자료
27. 다음의 함수를 구현하기
한줄 답변: Array.prototype.concat VS spread oprator
duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]
// concat 사용
function duplicate(arr) {
return arr.concat(arr);
}
// ES6 문법 활용:
const duplicate = (arr) => [...arr, ...arr];
28. 왜 Ternary 표현식이라 불리며 “Ternary”이라는 단어가 가리키는 것은?
한줄 답변: 삼항 연산자 표현식.
condition ? then : else
“Ternary”라는 단어는 “셋”을 의미한다. 그리고 Ternary 표현식은 3개의 연산자를 가진다.
첫째로 조건문, 두번째로 then 표현식, 세번째로 else 표현식을 가진다. 삼항 표현식은 자바스크립트에서만 사용되는 것은 아니며 다른 언어에도 존재한다.
condition ? exprIfTrue : exprIfFalse;
관련 자료
29. “use strict”가 무엇인가? 이것을 사용하는것의 장점과 단점은?
한줄 답변: 일부 기능을 제한한다는 의미. 오류 방지 목적을 가지며 단점보다는 장점이 많음
전체 코드나 함수에 strict mode를 적용하기 위해 사용한다. strict 모드의 선언은 자바스크립트 변형의 제한에 동의한다는 것이다.
장점
- 의도하지 않은 전역 변수가 선언되지 못하도록 한다
- 삭제할 수 없는 속성(property)를 삭제하려고 시도하면 오류를 발생시킨다
- 함수의 파라미터 이름은 서로 달라야 한다
this
는 전역 컨텍스트에서undefined
다- 몇몇 일반적인 코딩 실수를 잡아서 예외 처리(throw exception)시킨다 (예를 들어 전역 객체에 접근하려고 하는 것)
- 자바스크립트에서 개발자에게 혼란을 주거나, 잘못 만든 것으로 보이는 여러 기능의 사용을 금지한다
단점
- 못쓰게 되는 많은 기능들이 어떤 개발자에게는 필요한 기능일 수 있다
function.caller
그리고function.arguments
에 접근할 수 없다- 서로 다른 strict mode로 작성된 코드를 연결했을 때 오류가 발생할 수 있다.
전체적으로, 단점보다는 장점이 더 많은 것으로 보인다. 그리고 나는 strict 모드에서 사용하지 못하게 하는 기능이 그렇게 필요했던 적이 없다. 나는 strict 모드를 사용할 것을 추천한다.
관련 자료
30. 웹사이트의 전역 scope를 건드리지 않고 그대로 두는 것이 좋은 이유는?
한줄 답변: 변수와 함수 이름의 충돌을 방지하기 위해
모든 스크립트는 전역 스쿠프에 접근할 수 있다. 만약 모든 사람이 변수 선언에 전역 네임스페이스를 사용한다면 충돌이 매우 많이 발생할 것이다.
모듈 패턴(IIFE 등)으로 직접 선언한 변수는 로컬 네임스페이스에 포함되도록 해야 한다.
31. SPA 앱이 무엇인지, 그리고 SEO는 어떻게 해야 하는지
한줄 답변: 클라이언트 사이드 렌더링 웹사이트
최근 웹 개발자들은 그들의 제품을 웹 사이트보다는 “웹 앱”으로 부른다. 저 두 용어에 엄격한 차이는 없지만, 웹 앱은 더 상호작용성이 뛰어나고 동적이다. 유저들은 동작을 입력하는 즉시 반응을 얻을 수 있다. 전통적으로 웹사이트는 브라우저를 통해 서버에서 HTML을 받아서 렌더링하는 방식이었다. 사용자가 다른 URL로 이동하면 페이지 전체가 새로고침되고 서버가 이동할 URL에 맞는 새 HTML을 전달해준다. 이것을 서버 사이드 렌더링이라고 부른다.
하지만 최신의 SPA에서는 클라이언트 사이드 렌더링을 사용한다. 브라우저는 서버에서 초기 페이지와 함께 앱에 필요한 스크립트(프레임워크, 라이브러리, 앱 코드 등을 포함), 스타일시트를 내려받는다. 사용자가 다른 페이지로 이동하면 페이지 새로고침은 발생하지 않는다. 페이지의 URL은 HTML5 History API를 통해 기록된다. 새 페이지에 필요한 새 데이터는 브라우저에서 AJAX 리퀘스트를 통해 서버에서 보통 JSON 포맷으로 가져온다. SPA는초기 로딩때 가져온 자바스크립트를 사용해 데이터와 페이지를 동적으로 업데이트한다. 이 모델은 모바일 네이티브 앱이 동작하는 방식과 유사하다.
장점
- 사용자에게 앱이 보다 반응성있게 느껴지고 페이지 전환 사이에 화면 깜빡임과 새로고침 딜레이를 보지 않아도 된다.
- 서버에 더 적은 수의 HTTP 리퀘스트가 간다. 이미지같은 요소가 페이지마다 로딩될 필요가 없기 때문
- 서버와 클라이언트 사이의 확실한 역할 구분이 이뤄진다. 그렇기에 서버 코드의 수정 없이 다른 플랫폼(모바일, 챗봇, 스마트 시계 등)의 새로운 클라이언트를 쉽게 만들 수 있다. 또 API 스펙을 변경하지 않으면서 클라이언트와 서버의 기술 스택을 서로 독립적으로 변경할 수 있다.
단점
- 스크립트, 스타일시트 등 필요한 리소스가 많아서 초기 로딩이 더 무거워진다.
- 서버에서 모든 라우트로의 요청을 1개의 시작 라우트로 리다이렉트 되도록 설정해야 하고, 클라이언트 사이드에서도 라우팅을 직접 처리해야 한다.
- SPA는 컨텐츠의 렌더링을 자바스크립트에 의존한다. 하지만 모든 검색 엔진이 크롤링 중에 자바스크립트를 실행하지는 않는다. 이것은 검색 엔진 최적화(SEO)에 장애물이 된다. 하지만 대부분의 경우 앱을 만들 때 SEO는 가장 중요한 요소가 아니다. 그리고 모든 컨텐츠가 검색 엔진에 의해 인덱싱될 필요도 없다. SEO가 필요하다면 서버사이드 렌더링을 사용하거나 Prerender같은 서비스를 사용해 “자바스크립트로 브라우저에서 렌더링하고, HTML을 저장하고, 크롤러에게 제공”하도록 할 수도 있다.
관련 자료
- https://github.com/grab/front-end-guide#single-page-apps-spas
- http://stackoverflow.com/questions/21862054/single-page-app-advantages-and-disadvantages
- http://blog.isquaredsoftware.com/presentations/2016-10-revolution-of-web-dev/
- https://medium.freecodecamp.com/heres-why-client-side-rendering-won-46a349fadb52
32. Promise와 그것의 polyfill에 대해서는 얼만큼의 경험을 가지고 있는가?
한줄 답변: Promise는 비동기적으로 값을 리턴하는 객체다
Promise(프로미스)는 지금이 아닌 나중에, 비동기적으로 값을 리턴할 수 있는 객체다. resolve된 값이나, 또는 reject된 이유(예를 들어 네트워크 오류가 발생했다던가)를 받을 수 있다.
프로미스는 fulfilled, rejected, pending 3가지의 중 하나의 상태를 가진다. 프로미스를 사용할 때 fulfilled, reject 상태로 변경될 때 콜백을 전달 할 수 있다.
잘 알려진 polyfill로는 jquery $.deferred
, Q, Bluebird가 있지만 전부 표준 스펙을 만족시키진 못한다. ES2015는 프로미스를 지원하며 이제 polyfill은 필요하지 않다.
관련 자료
33. 콜백대신 프로미스를 사용하는 것의 장점과 단점은?
한줄 답변: 비동기 작업을 더 쉽게 구현할 수 있다는 장점. 단점은 딱히..?
장점
- 가독성 떨어지는 콜백 지옥에서의 탈출
.then()
을 사용해서 순차적인 비동기 작업을 가독성 있게 작성할 수 있다Promise.all()
을 사용해서 병렬로 실행되는 비동기 작업을 쉽게 작성할 수 있다-
프로미스가 있으면 콜백만 사용하는 코딩에서 발생하는 아래와 같은 일들이 발생하지 않는다
- 콜백을 너무 빨리 실행
- 콜백을 너무 늦게 실행(또는 실행하지 않음)
- 콜백을 너무 적게, 또는 너무 많이 실행
- 필요한 환경변수/파라미터의 전달 실패
- 확인해야 하는 에러, 예외가 숨어버린다
단점
- 조금 더 복잡한 코드
- ES2015를 지원하지 않는 구형 브라우저에서는 사용할 수 없으며 폴리필이나 Babel을 통한 컴파일이 필요하다.
34. 자바스크립트로 컴파일되는 언어를 사용하는 것의 장/단점
한줄 답변: 정적 타입 지원 등 자바스크립트의 한계를 보완해준다. 하지만 자바스크립트도 ES2015 이후 무척 좋아짐
CoffeeScript, Elm, ClojureScript, PureScript, and TypeScript 등의 언어를 사용하는 것을 말한다.
장점
- 자바스크립트가 가지고 있는 오랜 문제점의 해결, 자바스크립트가 가진 안티 패턴(anti-patterns, 객체 확장, 전역 스쿠프 사용, 적절하지 않은 방법의 truthy, falsey 값 확인 등)을 사용하지 않도록 만드는 것.
- 더 짧은 코드의 사용, 자바스크립트에 기반한 편리한 문법(syntactic sugar) 제공. (ES2015에서 많이 개선됨)
- (typescript를 사용하면) 오랜 기간 유지될 프로젝트에 정적 타입을 사용할 수 있어서 무척 좋다.
단점
- 브라우저만 자바스크립트만 지원하기 때문에 작성한 코드가 자바스크립트로 컴파일하는 과정이 추가로 필요하다
- 소스 맵이 pre-compile된 소스에 제대로 매핑되지 않아서 디버깅이 무척 힘들 수 있다
- 대부분의 개발자가 저들 언어에 익숙하지 않으며 학습이 필요하다. 결과적으로 당신의 프로젝트 관리를 위한 비용이 증가할 수 있다.
- 관련 커뮤니티가 작은 언어는 그만큼 관련 리소스, 튜토리얼, 라이브러리, 툴이 부족한다.
- IDE/editor 지원이 빈약할 수 있다(TypeScript 는 VSCode가 제대로 지원함. Flow 페이스북의 Nuclide 프로젝트가 있었지만 현재 retire함)
- 이들 언어는 항상 최신 자바스크립트 표준보다 뒤쳐져 있다.
- 개발자들은 자기가 작성한 코드가 무엇으로 컴파일되는지 알아야 한다 - 왜냐면 실제로 동작하는 것은 그 컴파일 코드고, 결국에는 그것이 중요한 것이다.
ES2015는 대단한 개선이 이뤄져서 코드를 작성하기에 무척 좋다. 요즘에는 CoffeeScript에 대한 수요를 거의 보지 못했다.
관련 자료
- Three JavaScript Anti-Patterns and How To Avoid Them
- https://softwareengineering.stackexchange.com/questions/72569/what-are-the-pros-and-cons-of-coffeescriptㅋ
35. 자바스크립트 개발에 어떤 디버깅 툴을 사용하는지?
한줄 답변: Chrome devtools, debugger, console.log, React, Redux Devtools
36. 객체 속성, 배열 항목 반복(iterate)에 어떤 방법을 사용하는가?
한줄 답변: 객체 -
for-in
+hasOwnProperty
,Object.keys
, 베열 -for-of
1. 객체
-
for-in 루프
for (var property in obj) { console.log(property); }
- 하지만 상속받은 속성도 포함한다. 그래서
obj.hasOwnproperty(property)
를 사용해서 검사해야 한다.
-
Object.keys(obj).forEach(property ⇒ { ... })
Object.keys
는 static 메서드이며 객체가 가진 열거 가능한(enumerable) 모든 속성을 배열로 만들어서 리턴한다.
-
Object.getOwnPropertyNames()
Object.getOwnPropertyNames(obj).forEach(property => { ... })
getOwnPropertyNames
는 객체가 가진 모든 열거 가능한, 열거 불가능한(non-enumerable) 값을 배열에 담아 리턴한다.
참조) enumarable, non-enumerable의 차이
enumerable 속성이란 내부적으로 enumerable 플래그가 true
값으로 지정되어 있는 속성이다. 그 값은 Object.getOwnPropertyDescriptor
메소드로 확인할 수 있다.
> var v = { a: 1 }
> Object.getOwnPropertyDescriptor(v, 'a')
{ value: 1, writable: true, enumerable: true, configurable: true }
enumerable 속성은 for ... in
루프를 실행했을 때 나타난다.
2. 배열
-
for
루프for (var i = 0; i < arr.length; i++)
- ES2015에서는 변수명 충돌을 방지하기 위해 var 대신 블럭 스쿠프 변수 키워드
let
을 사용하는 것을 추천한다.
-
forEach
arr.forEach(function (el, index) { ... })
- 이 방법은 index 변수가 필요없다면 굳이 선언하지 않아도, 코드에서 생략해도 되기 때문에 더 편리하다.
-
for - of
루프for (let elem of arr) { ... }
- ES6는 새로운 반복문을 제공한다.
for - of
문법은 Iteration protocols을 따르는 객체 (String, Array, Map, Set 등)로 반복문을 만들 수 있다. for 루프와 forEach 메소드의 장점을 조합한 것이라 할 수 있다. for 루프는 반복문을 중간에 멈추게 할 수 있고, forEach는 index 값이 필요없기 때문에 for 루프보다 더 간결한 문법을 가진다. for - of 문법은 둘의 장점을모두 가지고 있다.
for 루프보다는 forEach 메소드를 더 많이 사용한다. 하지만 이제 ES6를 사용할 수 있으므로, for - of 문법을 더 많이 활용한다.
만약 for - of 루프를 사용하면서 배열의 값과 인덱스가 모두 필요하다면, Array.prototype.entries
메소드와 구조분해할당(destructuring)을 사용하면 된다.
const arr = ["a", "b", "c"];
for (let [index, elem] of arr.entries()) {
console.log(index, ": ", elem);
}
관련 자료
- http://2ality.com/2015/08/getting-started-es6.html#from-for-to-foreach-to-for-of
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries
- Enumerability and ownership of properties - JavaScript | MDN
37. mutable, immutable 객체의 차이점
한줄 답변: 이미 선언된 값의 수정 가능, 불가능 여부
- mutability = 가변성, immutability = 불변성
- mutable = 가변의, immutable = 불변의
불변성은 함수형 프로그래밍에서 핵심 개념이다. 그리고 객체 지향 프로그래밍에도 많은 것을 제공한다. 가변(mutable) 객체는 만들어진 후에 그 값이 변경될 수 있는 객체다. 불변(immutable) 객체는 만들어진 후 변경될 수 없다.
자바스크립트에서 불변 객체의 예제
자바스크립트에서 몇몇 내장 타입(숫자, 문자열)은 불변이다. 하지만 커스텀 객체는 일반적으로 가변적이다. 불변 객체로는 Math
, Date
가 있다.
객체 상수 속성
writable: false
와 configurable: false
를 조합해서 객체 속성을 상수(변경, 삭제, 재할당이 불가능함)로 만들 수 있다.
let myObject = {};
Object.defineProperty(myObject, "number", {
value: 42,
writable: false,
configurable: false,
});
console.log(myObject.number); // 42
myObject.number = 43;
console.log(myObject.number); // 42
객체 확장 방지
객체에 새로운 속성이 추가되는 것을 방지하려면, Object.preventExtensions
메소드를 사용하면 된다.
var myObject = {
a: 2,
};
Object.preventExtensions(myObject);
myObject.b = 3;
myObject.b; // undefined
strict 모드에서는 속성 b
를 할당하는 과정에서 TypeError
오류가 발생한다.
Seal
Object.seal
메소드는 봉인된(sealed) 객체를 만든다. 새로운 속성을 추가할 수 없고, 속성을 제거하지도 새로운 값을 할당하지도 못한다. Object.preventExtension
메소드에 모든 속성의 configurable
값을 false로 만드는 기능을 더한 것이다.
configurable
이 속성의 값을 변경할 수 있고, 대상 객체에서 삭제할 수도 있다면 true
. 기본값은 false
.
Freeze
Object.freeze
메소드는 동결된(freezed) 객체를 만든다. 동결된 객체는 속성의 추가, 제거, 값 변경이 불가능하다. 그리고 동결된 객체는 프로토타입이 변경되는 것도 방지한다. 이 메소드는 자바스크립트 객체에 불병성을 부여할 수 있는 방법 중 가장 강력한 것이다.
var immutable = Object.freeze({});
Object.freeze
는 동결된 버전의 새 객체를 만드는 것이 아니라, 파라미터에 전달된 객체를 동결된 상태로 만들어서 그대로 반환한다.
장점
- 변경 탐지를 더 쉽게 할 수 있다. 객체 등가 비교가 레퍼런스 비교를 통해서 쉽고 빠르게 가능하다. 이는 React와 Redux에서 객체 변경을 확인하는 데 유용하다.
- 불변 객체를 사용한 프로그램은 파악하는데 덜 복잡하다. 왜냐하면 객체가 시간이 지나면서 바뀌는 상황을 고려할 필요가 없기 때문이다.
- 함수에 불변 객체를 파라미터로 전달하거나, 리턴값으로 받는다면 객체의 방어적 복사본(defensive copy)은 더 이상 필요하지 않다. 왜냐하면 전달한 객체가 함수 안에서 변경될 걱정이 없기 때문이다.
defensive copy. 원본과 동일한 복사본을 만드는 것. 함수에 원본의 레퍼런스를 전달하는 대신, 복사본의 레퍼런스를 전달한다. 함수를 실행 후에도 원본 값에는 영향을 주지 않기 위한 목적으로 만든다.
- 객체를 캐싱해서 여러 번 재사용할 수 있다.
- 스레드에서의 안전성 - 여러 스레드가 동시에 실행되는 환경에서도 값이 변경될 위험이 없으므로 안전하다.
- ImmutableJS 같은 라이브러리를 사용하면 여러 객체가 비슷한 구조를 공유하기 때문에 메모리를 더 적게 사용한다.
단점
- 매번 새로운 객체를 만드는 식으로 불변 데이터 구조를 가볍게 구현하면 퍼포먼스가 무척 나빠진다. 효율적인 불변 데이터 활용을 위해서는 라이브러리를 사용하는 것이 추천된다.
- 이미 있는 객체의 일부를 수정하지 않고 많은 작은 객체를 할당(allocation), 그리고 할당 해제(deallocation)한다면 성능에 문제를 야기할 수 있다. 할당자(allocator)와 가비지 컬렉터(garbage collector)의 복잡성은 보통 heap 메모리 영역에 있는 객체의 수에 따라 다르다.
- 그래프 같은 순환 데이터 구조는 만들기 어렵다. 만약 수정, 초기화도 될 수 없는 두 객체가 있다고 한다면 두 객체가 어떻게 서로를 참조하도록 할 것인가?
관련 자료
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
- https://stackoverflow.com/questions/1863515/pros-cons-of-immutability-vs-mutability
38. 코드를 작성할 때 불변성을 어떻게 구현하는지
한줄 답변:
const
, spread opeartor,Object.assign
,Array.prototype.concat
우선 immutable.js, mori, immer같은 라이브러리를 사용하는 방법이 있다.
대안으로는 const
를 사용한 변수의 선언과 객체 생성에 앞서 언급한 방법들을 조합하는 방법이 있다. 객체를 수정(mutation)을 위해서는 전개 연산자(spread operator), Object.assign
, Array.prototype.concat
등의 메소드를 사용해서 원본 객체를 수정하는 대신 새 객체를 만든다.
// Array Example
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // [1, 2, 3, 4]
// Object Example
const human = Object.freeze({ race: "human" });
const john = { ...human, name: "John" }; // {race: "human", name: "John"}
const alienJohn = { ...john, race: "alien" }; // {race: "alien", name: "John"}
관련 자료
- https://www.sitepoint.com/immutability-javascript/
- https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/
39. 동기, 비동기 함수의 차이점
한줄 답변: 코드의 순차적인 실행 vs 콜백에 전달된 코드를 나중에 실행
동기 함수는 블로킹(blocking)이 발생하지만, 비동기 함수는 그렇지 않다.
동기 함수는 앞에 있는 코드의 실행이 완료되어야 다음으로 넘어간다. 실행 순서가 보장되지만, 어떤 코드의 실행 시간이 길어진다면 프로그램이 멈춘 것처럼 보이게 될 것이다.
비동기 함수는 보통 콜백 함수를 파라미터로 받고 실행 즉시 다음 라인에 있는 코드로 넘어간다. 콜백은 비동기 작업이 완료되고 콜 스택이 비었을 때 실행된다. 웹 서버로부터 데이터를 불러오거나 데이터베이스 쿼리를 실행하는 것 같은 무거운 작업은 비동기적으로 실행되어야 한다. 그래야 메인 스레드가 그 긴 작업 시간동안 멈추지 않고 진행될 수 있다. 브라우저에서 저런 작업을 비동기로 하지 않는다면 UI가 사용자의 입력에 반응하지 않고 멈춰버릴 것이다.
40. 이벤트 루프란 무엇인가? 콜 스택과 태스크 큐의 차이점은?
한줄 답변: 동기 함수는 스택에 즉시 추가, 비동기 콜백은 태스크 큐에 대기하고 있다가 이벤트 루프가 스택에 추가
이벤트 루프는 싱글 스레드 루프(loop)로 콜 스택을 모니터링하면서 태스크 큐에 실행해야 할 작업이 있는지 확인한다. 만약 콜 스택이 비어있고 태스크 큐에 콜백 함수가 있다면, 함수는 큐에서 제거되고 실행을 위해 콜 스택에 추가(push)된다.
이벤트 루프에 대해서 더 자세한 내용은 유튜브에서 많은 조회수를 기록한 What the heck is the event loop anyway? 동영상을 보길 권한다.
console.log
같은 동기 함수는 스택에 바로 들어가고 실행된다. 하지만 비동기 web api인 setTimeout은 스택에 들어간 후 바로 지워지고, 대신 웹브라우저에서 타이머를 실행시킨다. 타이머가 끝나면, 콜백(cb
)을 태스크 큐에 추가하고, 모니터링하고 있던 이벤트 루프가 콜백을 스택에 추가해서 실행한다. 콜백 안에 비동기 함수가 있다면 비슷한 과정이 반복될 것이다.