면접대비2 - 26_Q&A
1. 일반적으로 CSS 태그를 태그 사이에 위치시키고, JS 태그를 태그가 끝나기 직전에 위치시키는 이유가 무엇인가요?
myAnswer 🍕
-
CSS
<link>태그를<head>태그 사이에 위치시키는 이유는 css<link>태그를<body>뒷부분에 사용할경우, HTML을 렌더링 한 후 CSS를 렌더링하기 시작하기 때문에, 랜더링이 총 두번 진행된다. 두번의 렌더링으로 낭비가 심하고 사용자에게 UX적인 측면에서 좋지 않다. -
JS
<script>태그를<body>태그가 끝나기 직전에 위치시키는 이유는 브라우저는<script>태그를 만나면 HTML 파싱을 잠시 멈추고<script>를 다운로드하고 실행한다. 이 경우 사용자에게 화면이 보여지기까지 시간이 늦어지기 때문에 , HTML요소를 모두 파싱한뒤에 자바스크립트 파일을 다운로드하고 실행할 수 있도록<body>태그 맨 뒤에<scripit>태그를 두는 것이 좋다.
2. script / script async / script defer 태그들의 차이점은 무엇인가요?
myAnswer 🍕
-
3개의 스크립트 태그는 해당 스크립트를 다운로드받은 뒤 실행하는 점은 동일하지만 이 동작을 시작하는 순서에 차이가 있다.
<script>는 HTML 파싱을 중단한 뒤 해당 스크립트를 즉시 다운로드 받고 실행한 뒤에 HTML 파싱을 재게한다.<script async>는 HTML파싱과 동시에 해당 스크립트를 다운로드 받지만 HTML 파싱에 완료되지 않아도 스크립트를 실행할 수 있다면 즉시 실행한다.<script defer>는<script async>와 동일하게 HTML 파싱과 동시에 해당 스크립트를 다운로드 받는 점은 동일하지만<script defer>는 HTML 파싱이 끝나야 스크립트를 실행하는 차이점이 있다. 또한 HTML 파싱이 된 후 스크립트가 실행된다는 점에서<script>태그를<body>태그가 끝나기 직전에 위치시키는 것과 거의 비슷한 효과를 가져온다.
3. 시맨틱 태그(sementic tag) 에 대해 설명하세요.
myAnswer 🍕
시맨틱 태그는 자신의 목적과 의미를 브라우저와 개발자에게 명확하게 설명하는 태그를 뜻한다. <div> 나 <span>은 자신이 무엇을 담고 있는 컨텐츠인지 그 자체로 설명하지 못하는 반면, <form>, <table> 은 자신이 각각 폼과 표를 담당하는 컨텐츠라고 설명한다.
시맨틱 태그를 사용 시 장점은
- 검색 엔진은 시맨틱 태그를 검색 랭킹에 사용할 중요한 지표로 활용
- 시각 장애인을 위한 화면 판독기로 페이지를 탐색할 때, 시멘틱 태그가 중요한 지표
- 의미없는 div를 클래스와 함께 무더기로 사용하는 것보다 적절한 시맨틱 태그를 사용하는 것이 개발할 때 더 편함
4. 개발자도구를 많이 사용하셨다면 주로 어떤 용도로 사용했나요?
myAnswer 🍕
-
Elements탭과 Styles : Elements탭과 Styles탭을 사용하여 HTML이나 CSS코드를 제가 사용하는 VSCode에서 변경하지 않고 현재 보고 있는 페이지를 편집하는데 사용 예를들어 position absolute를 활용하여 요소의 위치를 원하는 위치에 위치시키려 했지만 그게 안될때 원인을 찾기위해 부모요소의 css설정 값을 변경해보며 잘못된 부분을 찾을때 사용
-
consolelog탭 : 예를 들어 체크박스를 활용하여 체크된 아이템을 배열에 담아두었을때 리셋시키는 버튼을 구현한 경험이있습니다. 그럴경우 consolelog를 활용하여 체크된 요소들이 배열에 담겼는지 리셋버튼을 클릭 시 배열이 리셋되며 비어지는지를 확인할때 사용
-
application 탭 : 프로젝트 진행 시 로그인, 회원가입 기능을 구현할때 로컬스토리지에 유저 정보를 저장하여사용하였습니다. 또 특정 페이지에서 로컬스토리지에 담긴 유저정보 유무를 파악하여 해당 페이지 접근을 컨트롤할때 테스트하며 어플리케이션 탭을 활용
-
performance 탭 : 페이지를 로드하는 시간이 많이 소요되거나 버튼 클릭 시 브라우저가 멈추는 현상 등 사용자에게 부정적인 인상을 줄 수 있는 요소들을 체크하고 제거하기 위해 사용
-
network 탭 : 페이지에서 수행된 네트워크 작업에 대한 결과를 표시하기 때문에, 특정 네트워크 작업을 선택해, 해당 항목의 요청과 응답에 대한 상세 내용을 확인할때 사용
5. 웹팩과 바벨의 역할에 대해서 설명하세요.
myAnswer 🍕
웹팩
- 웹팩은 자바스크립트 어플리케이션을 위한 정적 모듈 번들러이다. 모듈은 우리가 작성한 자바스크립트 소스코드와 우리가 사용한 라이브러리들이다. 번들러는 의존성 있는 모듈을 하나의 파일로 통합시켜주는 도구이다. 우리 코드와 라이브러리들을 하나로 통합시키는 모듈 번들러가 필요한 이유는 다음과 같다.
-
웹페이지에서 자바스크립트 파일을 여러번 다운로드 받지 않게 하기 위해
웹 페이지를 로드할 때마다 각각의 모듈을 따로 다운로드 받는다면 네트워크 병목 현상이 일어날 수 있다. 그래서 각기 다른 자바스크립트 파일을 하나로 만들어, 한번에 다운로드 받을 수 있게 한다.
-
모듈 단위로 개발할 수 있게 해준다.
만약, 자바스크립트 파일 하나에 모든 코드를 담는다면 유지보수 하기 굉장히 어려울 것이다. 우리는 기능에 따라 코드를 모듈화 하여 우리가 이해하기 쉽도록 코드를 구분해 가독성과 유지보수성을 효율적으로 개선할 수 있다. 그리고 코드를 배포할 때는 자바스크립트 파일을 한번에 다운로드 할 수 있도록 웹팩이 코드를 하나로 합쳐주기 때문에, 모듈화로 인한 네트워크 병목 현상을 걱정할 필요가 없다.
-
바벨
- 바벨은 자바스크립트 트랜스파일러이다. 트랜스파일러는 하나의 언어로 작성된 소스코드를 비슷한 수준의 다른 언어로 변환하는 것을 의미한다.
-
구형 웹 브라우저에서도 사용할 수 있게 하기 위해
자바스크립트는 다양한 웹브라우저에서 사용되며, 웹 브라우저에서도 각기 다른 자바스크립트 엔진을 사용한다. 이 엔진들은 각기 다른 자바스크립트 버전을 지원한다. 가령 V8 엔진은 ES7을 지원하고, 차크라 엔진은 ES5까지만 지원한다고 가정하자. 만약 우리가 ES7 문법을 사용한다면 차크라 엔진은 우리가 작성한 자바스크립트 코드를 읽지 못하고 에러를 뱉어낼 것이다.
모든 자바스크립트 실행 환경에서 정상적으로 동작하게 하기 위해서는 바벨을 사용해 최신 자바스크립트 문법을 레거시 문법으로 변경시켜주어야 한다.
-
6. event.preventDefault() 의 역할이 무엇인지 설명하세요.
myAnswer 🍕
preventDefault 는 기본으로 정의된 이벤트를 작동하지 못하게 하는 메서드입니다.
<a>,<input>,<textarea>의 기본 동작을 막을 수 있습니다.
특정 이벤트가 트리거되면, 해당 이벤트에 수반되는 디폴트 액션을 취소시킵니다.
예) checkbox 요소의 디폴트 액션은 checkbox가 토글링 되는 것입니다. 만약, checkbox 요소의 click 이벤트 콜백함수에 preventDefault()를 선언하면, checkbox를 눌러도 checkbox가 토글링 되지 않습니다.
7. window.requestAnimationFrame(callback) 의 역할이 무엇인지 설명하세요.
myAnswer 🍕
window.requestAnimationFrame(callback)은 HTML5의 Canvas, SVG 등의 애니메이션 구현을 위해 사용하는 함수이다.
window.requestAnimationFrame(callback)은 비동기 함수로, 브라우저가 실행 시기를 결정하며, 스스로 반복해서 호출하지 않는다.
- 브라우저가 실행시기를 결정한다 : window.requestAnimationFrame(callback)은 애니메이션을 위해 만들어진 비동기 함수로, 우리가 window.requestAnimationFrame(callback)를 호출하면, 브라우저는 모니터의 주사율에 맞추어 함수를 실행한다. 가령, 모니터의 평균 주사율이 60FPS이면 1초에 60번 함수를 실행하는 것이다.
- 스스로 반복해서 호출하지 않는다 : window.requestAnimationFrame(callback)은 스스로 반복해서 호출하지 않기 때문에, window.requestAnimationFrame(callback) 함수로 다음 함수를 반복하려면 재귀적으로 window.requestAnimationFrame(callback) 함수를 다시 호출해주어야 한다.
또한 모니터의 주사율에 맞춰 실행 주기가 결정되기 때문에, 사용 환경에 따라 애니메이션이 완료되는 시기가 달라질 수 있다. window.requestAnimationFrame(callback)은 FPS를 직접 제어할 수 있는 옵션을 제공하지 않기 때문에 직접 함수 안에서 분기를 만들어 throttle을 걸어주어야 한다.
이 때문에 window.setInterval 함수가 더 편해보이지만, window.requestAnimationFrame(callback)의 장점은 콜백함수의 인자로 timestamp 값이 넘어온다. timestamp는 밀리세컨드 단위의 시간값이기 때문에, 복잡한 물리학 공식을 이용해 애니메이션을 구현해야 할 때는 window.setInterval 함수보다는 시간값이 제공되는 window.requestAnimationFrame(callback)가 편하다.
- 참고
8. intersection Observer API가 무엇인지 설명하세요.
myAnswer 🍕
-
Intersection Observer API의 등장 배경
- reflow와 repaint에 대한 간단한 설명 : 브라우저가 HTML과 CSS, JS 파일을 받으면, 그걸 해석해서 DOM 노드들을 만든다. 이렇게 만들어진 DOM 노드들은 트리 모양의 위계 질서를 가지기 때문에 렌더 트리라고 부른다. 따라서, 특정 DOM 노드의 레이아웃 수치(크기나 위치 등)가 변경되면 다른 노드들(자식이나 부모, 조상)도 영향을 받을 수 있다. 이렇게 변경된 노드를 포함해 영향을 받은 모든 노드들의 수치를 다시 계산하고, 렌더 트리를 재생성하는 과정을 **
Reflow**라고 한다. 그리고 재생성된 렌더 트리를 다시 그리는 과정을 **Repaint**라고 한다.
우리가 웹사이트를 개발할 때, 사용자가 특정 위치에 도달했을 때 액션을 취할 수 있도록 구현해야 하는 때가 있다.
- 페이지 스크롤 시 이미지를 lazy-loading 할 때
- 스크롤 시, 더 많은 컨텐츠가 로드되어 사용자가 페이지를 이동하지 않아도 되게 하는 infinite-scroll(무한 스크롤)을 구현할 때
- 광고의 가시성을 확인하여 광고 수익을 계산해야 할 때
- 사용자가 결과를 볼 것인지에 따라 애니메이션 동작 여부를 결정할 때
기존에는 특정 위치에 도달했을 때 어떤 액션을 취하도록 하기 위해서는
addEventListener()와scroll이벤트를 사용했다. 그러나 scroll 이벤트는 단시간에 수백, 수천번 호출될 수 있기 때문에, DOM을 변경하는 등의 무거운 작업을 수행하기는 어렵다. 또한 scroll 이벤트가 한 페이지 내 여러 요소에 등록되있을 경우, 사용자가 스크롤할 때마다 끊임없이 이벤트가 호출될 수 있다.그리고 특정 지점을 관찰하기 위해
getBoundingClientRect()함수를 사용해야 하는데, 이 함수는 잦은 reflow 현상이 발생한다는 단점이 있다.결론적으로, scroll 이벤트나 getBoundingClientRect() 를 사용할 경우, 우리가 기대하는 효과를 내기 위해 너무 많은 리소스가 들어간다.
- reflow와 repaint에 대한 간단한 설명 : 브라우저가 HTML과 CSS, JS 파일을 받으면, 그걸 해석해서 DOM 노드들을 만든다. 이렇게 만들어진 DOM 노드들은 트리 모양의 위계 질서를 가지기 때문에 렌더 트리라고 부른다. 따라서, 특정 DOM 노드의 레이아웃 수치(크기나 위치 등)가 변경되면 다른 노드들(자식이나 부모, 조상)도 영향을 받을 수 있다. 이렇게 변경된 노드를 포함해 영향을 받은 모든 노드들의 수치를 다시 계산하고, 렌더 트리를 재생성하는 과정을 **
-
Intersection Observer API란?
이러한 문제를 해결하기 위해, 2016년 Intersection Observer API가 등장하였다. Intersection Observer API는 관찰하고자 하는 타겟 요소가 조상 요소나 최상위 문서의 뷰포트(e.g. 브라우저의 viewport)의 교차영역에서 변경이 발생할 때마다 실행될 콜백 함수를 등록할 수 있게 한다.
Intersection Observer API는 정확히 몇 픽셀이 겹쳤는지, 어떤 픽셀이 겹쳤는지 알려주지는 않지만, “N% 정도 교차하는 경우 상호작용을 이루어야 한다” 수준의 일반적인 사용은 가능하다.
-
Intersection Observer의 사용
Intersection Observer API 콜백함수는 다음과 같은 상황에서 실행된다.
- 타겟 요소가 뷰포트나 특정 요소(API에서는 root 요소)와 교차하는 경우
- observer가 최초로 타겟을 관측하도록 요청받을 때마다
root 요소와 타겟 요소가 교차하는 정도를 intersection ratio라고 하며, 0.0~1.0 사이의 숫자로 표현한다.
-
Intersection Observer API 사용법
const io = new IntersectionObserver(callback[, options])
callback: 타겟 요소와 root 요소가 교차되었을 때 실행하는 함수entries: IntersectionObserverEntry 객체의 리스트observer: 콜백함수가 호출되는 IntersectionObserver
options:root: default 값은 브라우저의 뷰포트(null). observer의 대상으로 등록할 타겟 요소는 반드시 root의 하위 엘리먼트여야 한다.rootMargin: default 값은0px 0px 0px 0px. root 요소의 마진값으로, rootMargin에 따라 교차 영역을 확장하거나 축소할 수 있다.threshold: default 값은 0. root 요소와 타겟 요소가 얼마나 겹쳐야 observer를 실행하할지 설정한다. 0.0~1.0 사이의 값으로, 0.0인 경우 타겟 요소가 교차영역에 진입하는 시점에서 observer를 실행하며, 1.0인 경우, 타겟 요소 전체가 교차영역에 들어왔을 때 observer를 실행한다.
5. IntersectionObserverEntry
IntersectionObserverEntry 객체에는 다음과 같은 정보가 들어있다.
IntersectionObserverEntry.boundingClientRect: 타겟 요소의 정보IntersectionObserverEntry.rootBounds: root 요소의 정보 (root를 선언하지 않았으면 null)IntersectionObserverEntry.intersectionRect: 교차된 영역의 정보IntersectionObserverEntry.intersectionRation,IntersectionObserverEntry.isIntersecting,IntersectionObserverEntry.time등- 참고
9. performance API가 무엇인지 설명하세요.
myAnswer 🍕
performance API는 좀 더 정교하게 어플리케이션을 모니터링 할 수 있게 도와준다.
performance API의 대표적인 메서드로는 **performance.now()**가 있다. performance.now()를 통해 페이지를 로드한 이후 지난 밀리세컨드를 보여준다. 단위는 일반 Date가 아닌 DOMHighResTimeStamp이기 때문에, 최대 정밀도는 5µs이다.
또한 performance.now() 외에도 코드의 특정 지점에 대한 시간을 측정하여, 성능 테스트 도구처럼 사용하고, 사용자 지정 메트릭으로 활용할 수 있는 메서드들이 있다.
performance.mark(name)- 코드 내에 마킹을 하는 메서드
- performance buffer에 timestamp를 생성하여, 코드의 특정 부분을 실행하는 데 걸린 시간을 저장해둔다.
- 인자 name은 문자열로, 마킹을 식별하는 용도이다.
performance.measure(name, start_mark, end_mark)- 코드 내 특정 구간을 마킹하고 측정하는 메서드
- start_mark와 end_mark가 지정되면, 해당 구간을 실행하는 데 걸린 시간을 측정한다.
- start_mark가 undefined이면, 네비게이션 시작부터 특정 마킹까지 걸린 시간을 측정한다.
- end_mark를 지정하지 않으면 마킹부터 지금까지 걸린 시간을 측정한다.
측정값은 **performance entry buffer**에 저장된다. performance API에는 이 버퍼에 저장된 값을 꺼내오는 세 종류의 api를 제공한다.
performance.getEntries(): 버퍼에 저장된 모든 값을 보여준다.performance.getEntriesByName(name): 버퍼에서 특정 마킹을 가져온다.performance.getentiresByType(type): 버퍼에 저장된 값 중 특정 타입만 보여준다. (measure, mark)
10. Bundling이 무엇이며 왜 필요한가요?
myAnswer 🍕
번들링의 개념은 말 그대로 어떤 것들을 묶는다는 뜻을 가지고 있습니다.
자바스크립트로 코딩을 하다보면 작업하는 코드를 여러 파일로 나누게 되는 경우도 있고 npm 을 통해 다양한 모듈을 다운 받아서 사용하게 되는 경우도 발생합니다.
이렇게 만들어진 여러 파일들, 모듈들을 묶어주는 것을 번들링이라고 하며 웹팩이 대표적인 번들러의 예시입니다.
번들링의 장점은 다음과 같습니다.
-
이전에 각 파일들마다 서버에 요청을 하여 자원을 얻어와야했던 반면, 같은 타입(html, css, js 등)의 파일을 묶어서 요청/응답을 받기 때문에 네트워크 비용을 줄일 수 있습니다.
-
Webpack 4 버전 이상부터는 development , production 이 두가지의 mode 지원을 하면서, 특히 production 모드로 번들링을 진행할 경우, 코드 난독화, 압축, 최적화(Tree Shaking) 작업을 지원하기도 합니다.
-
Webpack의 주요 구성 요소 중 하나인 로더(Loder)가 일부 브라우저에서 지원이 되지 않는 ES6 형식의 자바스크립트 파일을 ES5로 변환하여 사용가능하게 해 줍니다. 웹 개발을 진행할 때 크롬과 같은 대중적인 브라우저만 고려하는 것이 아닌, 다른 모든 브라우저에 대해서도 커버가 가능합니다.
11. 이벤트 위임이 무엇인가요?
myAnswer 🍕
이벤트 위임(event delegation)이 무엇인가요?
- 이벤트 위임은 엘리먼트마다 핸들러를 할당하지 않고, 엘리먼트의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있는 방법을 말합니다.
- Javascript로 화면을 구현하다보면 동적인 요소를 다룰 일이 자주 발생하는데 이렇게 동적으로 노드를 생성하고 삭제할 때 각 노드에 대해 이벤트를 추가 하지 않고, 상위 노드에서 하위 노드의 이벤트를 제어 하고 싶을때 쓰는 방식입니다.
<div>
<h1>To do list</h1>
<button id="addBtn">add List</button>
</div>
<ul id="sampleTodoList">
<li id="list1">
List 1
</li>
<li id="list2">
List 2
</li>
<li id="list3">
List 3
</li>
</ul>h1{
font-size: 20px;
}
button{
font-size: 15px;
cursor: pointer;
}
li{
border: 1px solid;
background: #black;
font-size: 15px;
cursor: pointer;
}
li.remove{
text-decoration: line-through;
}let listAddBtn = document.getElementById('addBtn');
listAddBtn.addEventListener('click', addTodoList);
// listAddbtn 클릭시 todo list 추가
function addTodoList() {
let ulTag = document.getElementById("sampleTodoList");
let liTag = document.createElement("li");
let listOrder = ulTag.children.length + 1;
liTag.setAttribute("id", "list" + listOrder); // list4, list 5 ...
liTag.appendChild(document.createTextNode("List " + listOrder)); // List 4 ...
ulTag.appendChild(liTag);
}
// list에 줄긋기
// 새로 추가된 todoList는 줄이 그어지지 않는 문제 발생
let AllList = document.querySelectorAll('li');
AllList.forEach(function(li) {
li.addEventListener('click', deleteList);
});
function deleteList() {
let id = event.target.id;
document.getElementById(id).classList.toggle('remove');
}- 예를 들어 위와 같이 ‘List’가 3개가 있고 해당 목록을 추가 할수 있으며, 클릭하면
‘List’이렇게 줄이 그어지는 코드를 작성한다는 생각을 해보자. 아마 많은 사람들이 querySelectorAll 를 이용해 ‘List’가 적힌 li 태그를 잡아주고, 해당 태그를 클릭했을 때 줄을 그어주는 이벤트 리스너를 적용 시킬것 입니다. - 그런데 만약 3개가 아닌 ‘List’를 계속해서 추가 할 수 있고, 추가된 목록에 대해서도 줄을 그어야 한다면 어떨까요? li 태그에 이벤트 리스너를 추가하는 시점에 기존에 존재했던 todoList3개만 등록이 되고, 새롭게 동적으로 추가된 todoList는 이벤트 리스너가 등록되지 않았기 때문에 줄을 긋는 기능이 동작하지 않을 것 입니다.
// li태그의 상위 태그인 ul태그에 이벤트 리스너를 연걸하여
// 하위에서 발생하는 클릭 이벤트를 감지해준다.
let AllList = document.getElementById("sampleTodoList");
AllList.addEventListener('click', deleteList);
function deleteList() {
let id = event.target.id;
document.getElementById(id).classList.toggle('remove');
}- 이럴 때 이벤트 위임을 활용할 수 있습니다. 위에 보이는 코드와 같이 할일 목록이 적힌 태그의 상위 태그를 이벤트 리스너에 연결 하고, 하위에서 발생한 클릭 이벤트를 감지 하여 처리 하는 이벤트 위임 으로 해당 문제를 해결할 수 있습니다.
12. 이벤트 버블링이란 무엇이며 막을 수 있는 방법은 무엇인가요?
myAnswer 🍕
이벤트 버블링이란 무엇인가요?
이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성을 의미합니다.
<body>
<div class="one">
<div class="two">
<div class="three"></div>
</div>
</div>
</body>var divs = document.querySelectorAll('div');
divs.forEach(function(div) {
div.addEventListener('click', logEvent);
});
function logEvent(event) {
console.log(event.currentTarget.className);
}위와 같은 코드가 있을 때, console 창에는 three, two, one 의 순서대로 찍힙니다.
하위 요소의 이벤트가 최상위 요소까지 전달되는 것이죠.
버블링 중단하기
이벤트 버블링은 타깃 이벤트에서 시작해서 <html> 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생합니다. 몇몇 이벤트는 window 객체까지 거슬러 올라가기도 합니다. 이 때도 모든 핸들러가 호출됩니다.
그런데 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수도 있습니다.
이벤트 객체의 메서드인 event.stopPropagation()를 사용하면 됩니다.
참고: https://ko.javascript.info/bubbling-and-capturing
13. 스코프에 대해서 설명하세요.
myAnswer 🍕
자바스크립트에서 스코프는 변수가 유효할 수 있는 범위이며 일반적으로 중괄호로 감싸진 영역을 말합니다. 핵심만 말하자면, 스코프는 변수의 수명을 결정하고 확인할 수 있는 범위인 것입니다.
스코프는 크게 Local Scope와 Global Scope로 나눌 수 있습니다. Global Scope는 최상단의 스코프로써 이 곳에서 선언된 변수(전역 변수)는 어떤 영역에서든 접근이 가능합니다. Local Scope는 Global Scope에 포함되어 있는 영역으로 이곳에서 선언된 변수(지역 변수)는 전역(Global)에서 선언된 변수보다 더 높은 우선순위를 가집니다.
let age = 10;
function printAge(){
let age = 15;
console.log(age); // 15
}그리고 앞서 언급한 두 스코프에서의 변수들 간에는 반드시 지켜야 하는 규칙이 있습니다. 바로 각 영역에서 선언된 변수들끼리의 접근 가능 여부입니다. Local Scope에서 선언된 변수는 Global Scope에선 참조가 불가능합니다. 하지만 Global Scope에서 선언된 변수(전역 변수)는 Local Scope에서 참조가 가능합니다.
let global = 'Global';
function checkAccess(){
let local = 'Local';
console.log(global); // 'Global'
}
checkAccess();
local; // ReferenceError14. 클로져에 대해서 설명하세요.
myAnswer 🍕
closure 가 무엇인가요?
클로저는 내부함수의 변수가 외부함수의 변수에 접근할 수 있는 매커니즘입니다.
함수와 함수가 선언된 어휘적 환경(lexical environment)의 조합을 통해 만들어진 매커니즘인데요. 이 환경은 클로저가 생성된 시점의 유효 스코프 내에 있는 모든 지역 변수로 구성됩니다. 그래서 클로저가 포함된 내부 함수에서 외부 함수의 스코프에 접근할 수 있습니다.
일반적으로 함수가 실행될 때 생성된 컨텍스트는 함수가 종료될 때 가비지컬렉션의 수집대상이 되어 사라집니다. 하지만 클로져 패턴이 사용된 경우에는 내부함수의 변수가 언제 외부함수의 변수를 참조할지 알 수 없기 때문에 외부함수가 종료되어도 가비지 컬렉션의 수집대상이 되지않고 메모리상에 남아있게 됩니다. 이런 이유로 클로저 패턴을 남발하게 되면 메모리 누수가 발생하고 이로 인해 퍼포먼스 저하가 일어날 수 있습니다.
function outerFn(){
let outerVar = 'outer';
console.log(outerVar);
//클로저 함수
function innerFn(){
let innerVar = 'inner';
console.log(innerVar);
}
//클로저 함수 안에서는
//지역변수(innerVar)
//외부함수의 변수(outerVar)
//전역변수(globalVar)
//접근이 모두 가능합니다.
return innerFn;
}
let globalVar = 'global';
let innerFn = outerFn();
innerFn();closure 와 lexical environment가 어떻게 연관되는지 설명해주세요.
lexical environment (어휘적 환경)
- 어휘적 환경은 특정 코드가 작성, 정의된 환경을 의미합니다. 내가 사용하고자 하는 변수, 함수 등이 어떤 어휘적 환경에 속해 있는지에 따라 이용 가능한 변수가 달라지게 되는데, 어떤 변수나 함수의 값은 이를 ‘어떻게 호출했는지’ 가 아니라, ‘어디에서 정의했는지’ 즉 lexical scope가 어디인지에 따라서 결정됩니다.
function outer(){
let a = 1;
function inner(){
console.log(a);
}
inner();
}
outer(); // 1- 위의 예시를 보겠습니다.
*outer()의 실행 결과는 1입니다. outer 함수 내부에서 inner 함수를 호출하는데, inner함수에는 a가 없기 때문에 상위 스코프인 outer함수에서 a를 찾게 되는 것**입니다.
let a = 1;
function foo() {
let a = 10;
bar();
}
function bar() {
console.log(a);
}
foo(); // 1 or 10
bar(); // 1- 함수의 상위 스코프를 결정하는데에는 두 가지 방법이 있습니다.
- 동적 스코프(dynamic scope)로 함수가 호출되는 시점에 따라 상위 스코프를 결정하게 되는 경우
- lexical scope로 함수를 어디서 정의하였는지에 따라 상위 스코프를 결정하게되는 경우 입니다.
- 따라서 위의 예시의 경우
- 동적 스코프(dynamic scope) 라면
foo()의 실행 결과는 10 - lexical scope라면
foo()의 실행 결과는 1이 나오게 됩니다.
- 동적 스코프(dynamic scope) 라면
function outer(){
let name = 'kimcoding';
function inner(){
console.log(`hi ${name}!`); // 'hi kimcoding!'
}
inner();
return inner;
}
let greeting = outer();
greeting();- 한가지 예시를 더 보겠습니다. outer함수 내부에서 inner함수를 호출했을 때, lexical scope에 따라서 inner함수의 상위 스코프는 outer함수가 됩니다. 따라서 outer함수에 있는 name이라는 변수에 접근을 할수 있게 됩니다.
- greeting 이라는 변수에는 outer함수의 리턴값인 inner함수가 담겨있습니다. outer 함수는 이미 종료되어 콜스택에서 빠져 나갔지만,
greeting()을 실행해보면 여전히 name 이라는 변수에 접근해hi kimcoding!을 찍는 것을 확인할 수 있습니다. - 이처럼 어떤 함수를 lexical scope 밖에서 호출해도, 원래 선언이 되었던 lexical scope를 기억하고 접근할 수 있도록 하는 특성을 closure라고 부릅니다.
15. 클로져의 사용 예제를 알려주세요.
myAnswer 🍕
클로져를 이용한 대표적인 사용 예제들이 있습니다.
첫 번째, 캡슐화(정보 은닉)또는 클로저 모듈 패턴이라고 부르는 ****기법입니다. 캡슐화는 객체지향 프로그래밍 개념 중 하나이지만 함수형 프로그래밍에서도 클로져를 이용하여 변수를 은닉할 수 있습니다.
function checkNum(){
let num = 0;
return {
plus: function() {return num++},
minus: function() {return num--},
check: function (){return num}
}
}
let counterA = checkNum();
counterA.plus();
counterA.plus();
counterA.check(); // 2
let counterB = checkNum();
counterB.plus();
counterB.minus();
counterB.minus();
counterB.check(); // -1위의 counterA, counterB는 각각 다른 변수 num을 다루면서 밖으로 노출시키지 않습니다. 이와 같은 은닉화를 통해서 변수 num이 외부로부터 오염되는 것을 막을 수 있습니다.
두 번째, 커링(currying) 기법입니다. 커링은 여러 개의 인자를 가진 함수가 있을 때, 각각의 인자를 따로 받는 함수를 만들어서 호출하는 기법입니다. 함수 하나가 여러개의 인자를 받는 대신, 여러 개의 함수를 만들어 각각 인자를 받게 하여 마치 템플릿 함수처럼 사용할 수 있고 고정값을 넣어 재사용할 수 있습니다.
// a, b인자를 가진 함수
function multiply(a, b){
return a * b;
}
// 커링변환을 하는 함수
function curry(callback){
return function(a){
return function(b){
return callback(a, b);
}
}
}
//multiply라는 콜백함수가 저장되어 템플릿으로
//재사용할 수 있습니다.
//usage는 곱셈함수로 이용될 것입니다.
let usage = curry(multiply);
usage(2)(5); // 10
//3을 고정해놓고 재사용할 수 있습니다.
//form은 항상 3을 곱하게 될 것입니다.
let form = usage(3);
form(40); // 120
form(10); // 3016. 변수 선언, 초기화, 할당의 차이점에 대해서 설명하세요.
myAnswer 🍕
자바스크립트에서의 변수 선언(Declaration)은 실행 컨텍스트의 변수 객체에 변수를 등록하는 단계를 의미합니다. 이 변수 객체는 스코프가 참조하는 대상입니다. 한 마디로, 스코프에 변수를 등록하는 단계이며 이 단계에서 호이스팅이 일어납니다.
초기화(Initialization)는 실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계입니다. 이 단계에서 할당된 메모리에 undefined로 초기화 됩니다.
할당(Assignment)은 undefined로 초기화 된 메모리에 다른 값을 넣는 것입니다.
17. 호이스팅과 Temporal Dead Zone이 어떻게 연관되어있는지 설명하세요.
myAnswer 🍕
호이스팅은 var, let, const, function, class 키워드 등을 사용해서 선언하는 모든 식별자(변수, 함수, 클래스 등)이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징입니다.
우선 let,const와 var의 호이스팅 방식의 차이를 봐야합니다.
var의 경우 선언 단계와 함께 undefined로 초기화되므로 초기화 코드를 만나기 전부터 참조가 가능합니다.
반면에, let, const로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행됩니다. 즉, 스코프에 변수를 등록하지만, 초기화 단계는 변수 선언문 코드에 도달했을때 이루어지기 때문에 초기화 이전에 변수에 접근하려고 하면 참조 에러(Reference Error)가 발생합니다.
따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없는데, 이를 Temporal Dead Zone이라고 합니다.
따라서, let과 const는 호이스팅이 되기는 하지만 초기화가 이루어지지 않은 상태 (Uninitialized)에서 호이스팅이 되기 때문에 초기화 단계를 만나기 전에는 참조할 수가 없으며 일시적 사각지대 (TDZ)가 생기는 것입니다.
18. ES6의 주요 변화점에 대해서 설명하세요.
myAnswer 🍕
class vs prototype
자바스크립트는 설계 때부터 클래스라는 개념을 배제한 채 만들어졌기 때문에 ES6 이전까지 프로토타입(prototype) 기반의 객체지향프로그래밍을 지원하도록 만들어졌습니다.
그래서 객체지향프로그래밍을 하기 위해서는 프로토타입 기반의 코드를 작성해야 했지만 ES6 버전부터는 클래스 문법이 도입되었기 때문에 기존의 복잡한 코드가 아닌 훨씬 쉽고 간단한 코드로 객체지향프로그래밍을 할 수 있게 되었습니다.
let, const vs var
먼저 ES6 이전에 변수를 선언할 수 있는 방법은 var키워드가 유일했습니다. ES6 이후에 var키워드의 단점을 보완하기 위한 let, const키워드가 도입되었습니다.
var 키워드로 선언한 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정합니다. (function-level scope) 따라서 함수 외부에서 var 키워드로 선언한 변수는 코드 블록 내에서 선언하면 모두 전역변수가 됩니다. 따라서 함수 레벨 스코프는 전역 변수를 남발할 가능성을 높이는 단점이 있습니다.
반면에, let,const로 선언한 변수는 모든 코드 블록 (함수, if문, for문, while문, try/catch문 등)을 지역 스코프로 인정하는 블록 레벨 스코프(block-level scope)를 따릅니다.
let, const와 var 키워드의 차이점을 선언의 관점에서 설명하자면, let과 const키워드로 선언된 변수는 선언단계와 초기화 단계가 분리되어 진행되기 때문에 변수 선언문에 도달하기 전까지는 참조가 불가능합니다. 이 선언 단계에서부터 초기화 단계가 일어나기 전까지 변수 참조가 불가능한 구간을 Temporal Dead Zone이라고 부릅니다.
하지만 var키워드로 선언된 변수는 선언단계와 함께 undefined의 초기화 단계가 진행되므로 선언문 코드를 만나기전부터 참조가 가능합니다.
template literal
ES6 이전에는 작은따옴표나 큰따옴표를 이용해서 문자열을 표기하였지만 ES6 이후로는 코드 내에서 백틱을 사용하여 문자열을 표현하는 템플릿 리터럴 표기법이 새롭게 도입되었습니다. 새롭게 도입된 템플릿 리터럴 표기법은 두 가지의 큰 장점을 가집니다.
첫 번째, 기존의 일반적인 문자열에서는 줄바꿈이 허용되지 않고 줄바꿈의 공백(white-space)를 표현하기 위해서 백슬래시n(/n)과 같은 이스케이프 시퀀스(Escape Sequence)를 이용해야 했지만 템플릿 리터럴 내에서의 공백은 그대로 적용되므로 이스케이프 시퀀스를 사용할 필요가 없습니다.
두 번째, 템플릿 리터럴내에서 문자열 보관(string interpolation: ${})을 이용해서 표현식을 삽입할 수 있습니다. 이로 인해 더 이상 문자열 간에 연산자를 사용할 필요가 없게 되었으며 활용 면에서 보다 넓은 확장성을 갖게 되었습니다.
화살표 함수
ES6이전에는 Function키워드를 이용해서 함수를 선언하거나 함수 표현식을 이용해서 함수를 정의할 수 있었지만 ES6이후로는 화살표( => )를 이용해서 보다 간략한 방법으로 함수를 선언할 수 있게 되었습니다. 하지만 모든 경우에 사용할 수는 없으며 화살표 함수를 사용하기 위해 몇 가지 규칙이 필요합니다.
첫 번째, 화살표 함수는 익명 함수로만 사용이 가능합니다. 따라서 화살표 함수를 이용하여 함수를 정의하려면 함수 표현식을 사용해야 합니다.
두 번째, Function키워드로 선언된 일반함수는 함수가 어떻게 호출되었는지에 따라 this가 동적으로 결정됩니다. 하지만 화살표 함수의 this는 일반 함수에서와는 다르게 항상 상위 스코프의 this를 가리킵니다.
이 같은 규칙만 잘 지킨다면 함수를 선언함에 있어서 보다 간편하게 활용할 수 있습니다.
비구조화 할당
비구조화 할당 구문은 배열의 요소나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 ES6이후 도입된 표현식입니다. 비구조화 할당 구문을 다른 말로 구조분해 할당이라고도 부르는데 이를 적용한 몇 가지 예를 살펴보자면 다음과 같습니다. 첫 번째, 배열을 분해해서 변수에 전달하는 방법입니다. 변수를 배열로 묶어서 선언한다거나 매개변수를 배열로 묶어서 정의했을 때 배열을 전달하면 배열을 분해해서 변수에 전달할 수 있습니다.
const fullNameArr = ['min-su', 'kim'];
const [firstName, lastName] = fullNameArr;
console.log(firstName); // 'min-su'
console.log(lastName); // 'kim'
const fullName = [];
function func([first, last]){
fullName.push(first);
fullName.push(last);
}
func(fullNameArr);
console.log(fullName); // ["min-su", "kim"]두 번째, 객체를 분해해서 변수에 전달하는 방법입니다. 중괄호를 이용해 변수를 선언하고 객체를 할당하면 객체의 해당 변수이름의 속성값이 변수에 할당됩니다. 그리고 만약 객체의 속성이 변수로 선언되어 있다면 속성값을 생략해도 값을 할당할 수 있습니다.
const person = { name: 'min-su', age: 30 };
const { age } = person;
console.log(age); // 30;
const married = false;
const maleA = { ...person, married };
console.log(maleA); // {name: "min-su", age: 30, married: false}default parameter, rest parameter, spread syntax
ES6 이전에는 parameter를 판단하기 위해서 항상 함수 내부에서 인자를 이용해야 했지만 ES6이후부터는 매개변수에 기본값을 정의해주고 싶을 때 default function parameter를 다음과 같이
function func(parameter1 = undefined, parameter2){
}매개변수에 바로 등호를 사용해서 이용할 수 있습니다.
rest parameter는 정해지지 않은 여러 인수들을 매개변수로 전달했을 때, 배열로 받아 나타낼 수 있게 하는 구문입니다.
function restParameter(...parameter){
console.log(parameter);
}
restParameter('hi', 'hello', 'wolrd')
// ["hi", "hello", "wolrd"]**spread syntax(전개문법)**는 여러 개의 배열 또는 객체를 ’…’을 이용하여 병합하거나 배열의 요소들을 복사하여 여러인자를 가진 함수에 전달할 때 사용할 수 있는 문법입니다.
const spread = [3, 4, 5];
const arr = [1, 2, ...spread, 6];
console.log(arr) // [1, 2, 3, 4, 5, 6]
function sum(a, b, c) {
return a + b + c;
}
console.log(sum(...spread)); // 12for … of
ES6부터 배열 같은 반복가능한 객체에서 그 길이에 맞게 각 속성값에 대한 반복문을 실행하는 문법이 도입되었습니다.
const forOfExample = ['depression', 'pleasure', 'happiness'];
for (const element of forOfExample) {
console.log(element);
}
//"depression"
//"pleasure"
//"happiness"19. 원시 자료형, 참조 자료형
myAnswer 🍕
원시 자료형
자바스크립트가 제공하는 데이터 타입 중, 변경 불가능한 값(immutable value)을 원시 타입의 값(원시 자료형)이라고 말합니다.
6가지의 원시 자료형(string, number, boolean, undefined, null, symbol)이 있습니다. 이런 원시자료형은 변수가 할당될 때, 메모리의 고정 크기로 값을 저장하고 해당 주소를 직접 참조 합니다. 또한, 앞서 말했듯 **불변성(immutable)**이라는 특징을 가지고 있습니다.
그럼 이 불변성(immutable)이라는게 무엇 일까요? 코드를 통한 간단한 예시를 보도록 하겠습니다.
let a = 10;
a = 1;원시자료형의 변수에 메모리를 할당하는 방식은 아래와 같습니다.
변수 a 선언시 메모리에 공간을 할당 받습니다. 이후 a의 값의 수정을 거치고 나면 기존에 a가 가지고 있던 메모리의 값이 변경되는 것이 아닌 새로운 공간을 할당 받아 값을 가지게 됩니다. 이렇게 변수 값에 대한 수정이 일어나더라도 새로운 메모리를 할당 받습니다. 불변성(immutable)이란 이런한 것을 의미합니다.
불변성(immutable)의 예시
let str = 'letter'
str[0] = 'L';
console.log(str); // 'letter'- 원시자료형인 문자열은 유사 배열 객체라 불립니다. 유사 배열 객체란 마치 배열처럼 index를 통해 프로퍼티 값에 접근 할수 있으며 length를 가지는 객체를 뜻합니다. 즉, 문자열은 배열 처럼 index를 통해 각 문자에 접근할 수 있고 for문을 통해 index를 순회할 수 있다는 의미입니다.
- 위의 코드를 한번 보겠습니다. 앞서 말했듯, 문자열은 유사 배열 객체이기 때문에 index에 접근을 할 수 있습니다. 그러나 변수 str의 0번째 인덱스의 값을 대문자 ‘L’로 변경하더라도, 문자열은 변경 불가능한 값이기 때문에 변경한 값이 반영되지 않습니다. 따라서 예기치 못한 변경으로 부터 자유로울 수 있습니다. 이러한 원시자료형의 특성인 불변성(immutable)은 데이터의 신뢰성을 보장합니다.
참조 자료형
객체 (참조) 타입의 값. 즉 **변경 가능한 값(mutable value)**을 뜻합니다. 자바스크립트가 제공하는 데이터 타입 중, 원시 자료형이 아닌 모든 자료형을 참조자료형 이라 부릅니다. 자바스크립트의 참조 자료형에는 대표적으로 객체,배열, 함수가 있습니다. 참조자료형의 변수는 Heap이라는 영역에 생성됩니다. Heap 메모리의 값은 값이 할당된 메모리의 주소를 참조합니다.
let a = [1,2,3,4,5];
let b = a;
b[2] = 2;
console.log(a) // [1,2,2,4,5]두번째 줄까지 코드가 실행 되었을 때 변수 a와 b는 같은 메모리 주소를 참조하고 있습니다.
세번째 줄에서 배열의 값이 변경 되었을 때, a와b는 같은 메모리를 참조하고 있기 때문에 b를 수정하면 a가 참조하는 값의 내용도 변하게 됩니다.
20. == vs === 의 차이는 무엇인가요?
myAnswer 🍕
- ==( 동등 비교 연산자) 와 === (일치 비교 연산자) 는 2개의 피연산자가 같은 값인지를 비교하여 boolean값을 리턴합니다.
- 이 두개의 비교 연산자는 비교하는 엄격성의 정도가 다릅니다.
자바스크립트에서 == 는 무엇인가요?
- 동등 비교 연산자로써 좌항과 우항 피연산자의 ‘값’으로 동등함을 비교하며, 암묵적 타입 변환 (type coercion)을 통해 타입을 일치시킨 후 값의 동등함을 비교(느슨한 비교)합니다.
- 즉, 좌항과 우항 피연산자의 타입이 다르더라도 암묵적 타입 변환 후 같은 값일 수 있다면 true를 반환하게 됩니다.
암묵적 타입 변환 (type coercion)이란 무엇인가요?
- 하나의 데이터 타입이 다른 데이터 타입으로 자동적으로 혹은 암시적으로 변환되는 것을 말합니다.
암묵적 타입 변환 (type coercion)은 언제 발생 하나요?
- 암묵적 타입 변환환은 각기 다른 타입의 값들을 비교, 산술, 문자열, 논리 연산자로 연산 할때 발생합니다.
== (동등 비교 연산자) 예시
- Number 와 Boolean 타입 비교 0과 false의 값이 동일 하므로 true를 반환합니다.
0 == false // true- Number 와 String 타입 비교 압묵적 타입변환을 통해 타입을 일치 시킨 후 값이 동일 하므로 true를 반환합니다.
10 == "10" // true자바스크립트에서 === 는 무엇인가요?
- 일치 비교 연산자로써 좌항과 우항 피연산자의 **값과 타입을 모두 고려하여 동등함을 비교(엄격한 비교)**하는 연산자 입니다.
- 좌항과 우항 피연산자의 ‘값’과 ‘타입’으로 동등함을 비교합니다.
=== (일치 비교 연산자) 예시
- Number 와 Boolean 타입 비교 두 피연산자의 타입이 다르기 때문에 false를 반환합니다.
0 === false // false
console.log(typeof 0); // number
console.log(typeof false); // boolean- Number 와 String 타입 비교 두 피연산자의 타입이 다르기 때문에 false를 반환합니다.
10 == "10" // false
console.log(typeof 10); // number
console.log(typeof "10"); // string21. 자바스크립트에서 배열의 타입은 무엇인가요?
myAnswer 🍕
자바스크립트에서 배열의 타입은 왜 object인가요?
- 자바스크립트는 원시자료형(primitive type)을 제외하고는 모든것이 객체(object)로 이루어져 있습니다. 자바스크립트는 프로토타입 기반의 언어이기 때문입니다.
그렇다면 프로토 타입은 무엇인가요?
우선 프로토 타입에 대해 얘기하기전 객체지향 프로그래밍에 대해 먼어 알아보도록 하겠습니다. 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료 구조를 객체(object)라 부르는데 이러한 객체들의 집합으로 표현하려는 프로그래밍 패러다임을 객체지향 프로그래밍이라 부릅니다
객체 지향 프로그래밍
- 상속
- 객체지향 프로그램의 핵심 개념으로 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 뜻합니다.
- javascript는 프로토타입을 기반으로 생성자 함수를 통해 인스턴스를 생성할 때 상속을 구현하여 불필요한 중복을 제거할 수 있습니다.
- 상속은 코드의 재사용이란 관점에서 매우 유용합니다. 생성자 함수가 생성할 모든 인스턴스가 공통적으로 사용할 프로퍼티나 메서드를 프로토타입에 미리 구현해 두면 생성자 함수가 생성할 모든 인스턴스는 별도의 구현 없이 상위(부모) 객체인 프로토타입의 자산을 공유하여 사용할 수 있게 됩니다.
프로토타입
- 다시 돌아와 프로토 타입에 대해 알아보도록 하겠습니다. 프로토타입은 사전적인 의미로는 자신을 만들어낸 객체의 ‘원형’ 이라고 말할 수 있습니다.
- 이러한 프로토 타입 기반 언어인 자바스크립트는 객체 원형인 프로토 타입을 이용하여 새로운 객체를 만들어 냅니다.이렇게 생성이 된 객체 역시 또 다른 객체의 원형이 될 수 있습니다.
- 프로토 타입은 아래와 같이 해석이 되곤 합니다.
- 프로토 타입 객체를 참조하는
prototype프로퍼티 (prototype프로퍼티 ⇒ 생성자 함수가 생성할 인스턴스의 프로토 타입) - 아래의 예시를 보겠습니다.
- 프로토 타입 객체를 참조하는
// 생성자 함수
function Car() {
this.wheels = 4;
}
let bmw = new Car();
let volkswagen = new Car();
console.log(bmw.wheels); // => 4
console.log(volkswagen.wheels); // => 4- bmw와 volkswagen은 각각 wheels을 4개씩 공통적으로 가지고 있습니다. 어딘가에 담아두었다가 1번만 쓰면될걸 메모리에 2개가 할당이 되게 됩니다. 여기서 한번 객체의 수가 1억개가 된다고 생각해 봅시다. 어떤가요? 1억개의 변수가 메모리에 할당되게 되는 아주 비효율적인 일이 발생 한 것입니다. 이러한 문제를 상속을 통해 불필요한 중복을 제거하는것으로 해결할 수 있습니다.
function Car() {}
// Car 생성자 함수가 생성한 모든 인스턴스가 wheels 메소드를
// 공유해서 사용할 수 있도록 프로토타입에 추가합니다.
// 프로토타입은 Car 생성자 함수의 prototype 프로퍼티에 바인딩되어 있습니다.
Car.prototype.wheels = 4;
let bmw = new Car();
let volkswagen = new Car():
console.log(bmw.wheels); // => 4
...- 위 예시에서 console.log를 한번 봅시다. 어디선가 많이 보던 방식으로 조회하고 있지 않나요? 그렇습니다. 자바스크립트의 배열도 같은 방식으로 동작합니다. 자바스크립트는 프로토타입 기반의 언어이기 때문입니다.
- Car 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는프로토타입 Car.prototype으로부터 wheels 메소드를 상속받습니다.
- 즉, Car 생성자 함수가 생성하는 모든 인스턴스는 하나의 wheels 메소드를 공유하게 됩니다.
- 이렇게 되면 필요할때마다 공유 되어있는 wheels 메소드를 쓰면 되기 때문에 메모리를 크게 차지할 일도 줄어들게 됩니다.
22. undefined와 null 그리고 undeclared의 차이
myAnswer 🍕
undefined와 null 그리고 undeclared의 차이점은 무엇인가요?
-
undefined
let a; console.log(a); // undefined- undefined의 사전적 의미는 “정의되지 않은”입니다.
- 자바스크립트의 undefined에서 말하는 ‘정의’는 변수에 값을 할당하는 것을 뜻합니다.
- 위에 코드에서 볼수 있듯이 javascript에서의 undefined란 의도적으로 할당하기 위한 값이 아닌 자바스크립트 엔진이 변수를 초기화 할때 사용하는 값임을 알 수 있습니다.
- 만약 변수를 참조 하였을때 undefined가 반환이 된다면 참조한 변수가 선언 이후 값이 할당되지 않은 상태, 즉 초기화되지 않은 변수라는 것을 알 수 있습니다.
-
null
let red = 'apple'; red = null- 자바스크립트에서 변수에 값이 없다는 것을 의도적으로 명시하고 싶을때 사용됩니다.
- 비어있다 라는 내용(null)을 변수에 할당해 놓은 상태를 뜻합니다. 즉, null은 어떠한 변수에 ‘아무런 값도 나타내지 않겠다’ 라는 의도를 가지고 일부러 넣은 값을 의미합니다.
- 위의 코드를 예시로 보면 이전에 참조하던 값(‘apple’)을 더 이상 참조하지 않겠다는 의미 입니다.
- 함수가 조건에 부합하는 값을 검색할 수 없는 경우 null을 반환하기도 합니다.
- 명시적으로 할당됐다는 점에서 undefined와는 다릅니다.
-
undeclared
console.log(c) // Uncaught ReferenceError: c is not defined console.log(typeof c) // undefined- 접근 가능한 스코프 내에 변수의 선언도 할당도 되어있지 않은 상태를 말합니다.
- 실제로 undeclared 인 경우에도 type을 찍어보면 undefined 라고 반환되어 브라우저가 오류 처리를 하지 않도록 도와줍니다.
23. rest parameters와 spread syntax의 차이는 무엇인가요?
myAnswer 🍕
rest parameters
- rest parameters는 매개변수 이름 앞에 세개의 점을 붙여 정의한 매개변수를 의미합니다.
- rest parameters는 아래 코드에서 볼 수 있듯이, 함수로 전달된 인수들의 목록을 배열로 압축할때 사용됩니다.
- 일반 매개변수와 rest parameters는 함께 쓸 수 있으며, 함수에 전달된 인수들은 매개변수 및 rest parameters에 순차적으로 할당됩니다.
- rest parameters는 먼저 선언된 매개변수에 할당된 인수를 제외하고 나머지 인수들을 배열로 만들어 주기때문에 반드시 마지막 파라미터로 사용해야 합니다.
function restSyntax(...rest){
console.log(rest); // [1,2,3,4]
}
restSyntax(1,2,3,4);
function restSyntax(prameter,...rest){
console.log(prameter) // 1
console.log(rest) // [2,3,4]
}spread syntax
- spread syntax는 뭉쳐있는 여러개의 값들을 개별적인 값들의 목록으로 만들때 사용됩니다.
- spread syntax는 for ..of 문으로 순회할 수 있는(대표적으로는 배열과 문자열이 있습니다) iterable(순서가 있는)에 한정됩니다.
- spread syntax의 결과는
값이 아닌값들의 목록 입니다. 따라서 spread syntax의 결과는 변수에 할당 할 수 없습니다.
console.log(...[1,2,3,4,5]) // 1 2 3 4 5
console.log(...'Apple'); // A p p l e
// iterable이 아닌 객체에는 spread syntax를 사용할 수 없습니다.
console.log(...{ a: 1, b: 2 }); // TypeError: Found non-callable @@iterator24. 깊은 복사와 얕은 복사의 차이에 대해서 설명하세요. 자바스크립트에서 깊은 복사를 하는 방법은 무엇인가요?
myAnswer 🍕
- 자바스크립트의 두 가지 값의 형태인 원시값, 참조값 중 참조값인 객체를 복사할 때 나타나는 차이점입니다.
깊은 복사 (Deep copy)
- 깊은 복사된 객체는 객체가 중첩되어 있는 상황일 때 내부 객체까지 모두 새로 생성된 것을 의미합니다.
- 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어져 각각의 메모리에 할당이 되어 있습니다.
- 복사된 A객체와 B객체 중 어느 하나를 수정해도 다른 객체에 영향을 미치지 않습니다.
- 재귀함수를 이용한 복사
- JSON.stringify()
- 라이브러리 사용 (lodash)
얕은 복사 (Shallow copy)
- 얕은 복사란 가장 상위 객체만 새로 생성되고 내부 객체들은 참조 관계인 경우를 의미합니다.
- 같은 주소값을 가리키기 때문에 복사된 A객체와 B객체 중 어느 하나를 수정하면 다른 객체에 영향을 미칩니다.
- Object.assign()
- Spread Operator
25. let, const, var의 차이와 각각의 사용 방법을 설명하세요.
myAnswer 🍕
- 먼저 ES6 이전에 변수를 선언할 수 있는 방법은 var키워드가 유일했습니다. ES6 이후에 var키워드의 단점을 보완하기 위한 let, const키워드가 도입되었습니다.
선언과 할당
- var는 변수의 중복 선언이 가능해 의도치 않게 변수 값의 변경이 일어날 가능성이 커서 지양하는 방법입니다.
- let은 재선언이 불가능하지만 재할당은 가능합니다.
- const 는 변수 재선언, 재할당 모두 불가능합니다.
- let, const와 var 키워드의 차이점을 선언의 관점에서 설명하자면, let과 const키워드로 선언된 변수는 선언단계와 초기화 단계가 분리되어 진행되기 때문에 변수 선언문에 도달하기 전까지는 참조가 불가능합니다. 이 선언 단계에서부터 초기화 단계가 일어나기 전까지 변수 참조가 불가능한 구간을 Temporal Dead Zone이라고 부릅니다.
- 하지만 var키워드로 선언된 변수는 선언단계와 함께 undefined의 초기화 단계가 진행되므로 선언문 코드를 만나기전부터 참조가 가능합니다.
스코프
- var 키워드로 선언한 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정합니다. (function-level scope) 따라서 함수 외부에서 var 키워드로 선언한 변수는 코드 블록 내에서 선언하면 모두 전역변수가 됩니다. 따라서 함수 레벨 스코프는 전역 변수를 남발할 가능성을 높이는 단점이 있습니다.
- 반면에, let,const로 선언한 변수는 모든 코드 블록 (함수, if문, for문, while문, try/catch문 등)을 지역 스코프로 인정하는 블록 레벨 스코프(block-level scope)를 따릅니다.
사용 방법
- 불변의 값에 대해서는 const 를 사용하는 것은 코드의 가독성을 높이고 유지보수에 도움이 되기에 적절한 고정값에 대해서 적극적으로 사용하는 것이 바람직합니다. 재할당이 필요없는 변수를 let 으로 선언한다면 개발자가 실수로 그 값을 변경할 가능성이 있기에 재할당이 필요없는 변수는 모두 const 로 선언하는 것이 좋습니다.
26. 순수함수란 무엇인가요?
myAnswer 🍕
Pure Function (순수 함수)
- 순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미합니다.
- 외부 상태에 의존하지 않고, 오직 매개 변수를 통해 함수 내부로 전달된 인수에만 의존해 반환 값을 만듭니다.
- 또한 순수 함수는, 매개변수로 전달된 값을 수정하지 않습니다.
- 그래서 side-effect를 갖지 않는다는 장점이 있습니다.
- 순수 함수의 조건
- 동일한 인자값을 받으면 항상 동일한 리턴값을 반환한다.
- 어디서 호출되든 동일한 결과를 보여준다.
- 외부에 영향을 주지도 받지도 말야아 한다
순수함수 예제
function add(a, b) {
return a + b;
}
순수함수가 아닌 예제
var c = 5;
function add2(a, b) {
c = b;
return a + b + c;
}
// c의 값이 b가 무엇이 들어오느냐에 따라 달라져서 외부에 영향을 줘버릴 뿐만 아니라
// 외부 변수인 c에 따라 값이 변함