logo BLOG

70 자바스크립트 인터뷰 질문 - 1부(번역)

70 JavaScript Interview Questions

javascript, interview, translation


출처

70 JavaScript Interview Questions

시작하며

글이 긴 관계로 4부로 나눠서 진행하며 1부는 1-17번 질문, 2부는 18-36번, 3부는 37-54번, 4부는 55-70번 질문으로 구성되어 있다.

Hi Guys Good Day and a Happy New Year 🎆🎆🎆!

이 글은 긴 내용이므로 한두 시간 동안 나와 함께하시길 바랍니다. 모든 질문에 대한 답변에는 질문 목록으로 돌아가는 화살표 ↑ 링크가 있어서 스크롤 하는 시간을 낭비하지 않아도 됩니다.

질문

1. undefined와 null은 무엇이 다른가?

↑ undefined와 null의 차이점을 이해하기 전에 이들의 유사점을 이해해야 합니다.

  • 이들은 JavaScript의 7가지 원시 타입에 속합니다.
let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint'];
  • 이것은 falsy 값입니다.
  • Boolean(value) 또는 !!value를 사용해서 boolean로 변환할 때 false로 평가된 값입니다.
console.log(!!null); //logs false
console.log(!!undefined); //logs false

console.log(Boolean(null)); //logs false
console.log(Boolean(undefined)); //logs false

Ok, 차이점에 관해 얘기해봅시다.

  • undefined는 특정 값이 할당되지 않은 변수의 기본값입니다. 또는 명시적인 리턴 값이 없는 함수 예를들어 console.log(1). 또는 객체에 존재하지 않는 속성. 자바스크립트 엔진은 이를 위해 undefined 값을 할당합니다.
let _thisIsUndefined;
const doNothing = () => {};
const someObj = {
  a : "ay",
  b : "bee",
  c : "si"
};

console.log(_thisIsUndefined); //logs undefined
console.log(doNothing()); //logs undefined
console.log(someObj["d"]); //logs undefined
  • null은 **“값을 나타내지 않는 값”**입니다. null은 변수에 명시적으로 정의된 값입니다. 이 예제에서는 fs.readFile 메서드가 에러를 던지지 않으면 null 값을 얻습니다.
fs.readFile('path/to/file', (e,data) => {
  console.log(e); // 에러가 없을 때 로그 null
  if (e) {
    console.log(e);
  }
  console.log(data);
});

null과 undefined를 비교할 때 ==를 사용하면 true를, ===을 사용하면 false를 얻습니다. 여기서 확인 할 수 있습니다.

console.log(null == undefined); // logs true
console.log(null === undefined); // logs false

2. && 연산자는 무엇을 하는가?

↑ && 또는 논리 AND 연산자는 피연산자에서 첫 번째 falsy 표현식을 찾아서 리턴하며, falsy 표현식을 찾지 못하면 마지막 표현식을 리턴합니다. 불필요한 작업을 방지하기 위해 단락(short-circuiting)을 사용합니다. 저는 저의 프로젝트 중 하나에서 데이터베이스 연결을 닫을 때 이것을 catch 블록에서 사용했습니다.

console.log(false && 1 && []); //logs false
console.log(" " && true && 5); //logs 5

if 문 사용.

const router: Router = Router();

router.get('/endpoint', (req: Request, res: Response) => {
  let conMobile: PoolConnection;
  try {
      //do some db operations
  } catch (e) {
    if (conMobile) {
      conMobile.release();
    }
  }
});

&& 연산자 사용.

const router: Router = Router();

router.get('/endpoint', (req: Request, res: Response) => {
  let conMobile: PoolConnection;
  try {
     //do some db operations
  } catch (e) {
    conMobile && conMobile.release()
  }
});

3. || 연산자는 무엇을 하는가?

↑ || 또는 논리 OR 연산자는 피연산자에서 첫 번째 truthy 표현식을 찾아 리턴합니다. 이것 또한 불필요한 작업을 방지하기 위해 단락(short-circuiting)을 사용합니다. ES6 Default function parameters가 지원되기 전에 함수 안에서 기본 매개 변수 값 초기화를 위해 사용되었습니다.

console.log(null || 1 || undefined); //logs 1

function logName(name) {
  var n = name || "Mark";
  console.log(n);
}

logName(); //logs "Mark"

4. string을 number로 변환하는 가장 빠른 방법은 + 또는 단항 더하기 연산자를 사용하는 것인가?

↑ MDN 문서에 따르면 + 는 이미 number일 경우 값에 대한 작업을 수행하지 않기 때문에 string을 number로 변환하는 가장 빠른 방법입니다.

5. DOM 이란 무엇인가?

↑ DOM은 Document Object Model의 약자로 HTML 및 XML 문서를 위한 인터페이스 (API)입니다. 브라우저가 HTML 문서를 처음 읽을 때 (구문 분석할 때) HTML 문서를 기반으로 하는 큰 오브젝트를 만드는데 이것이 DOM입니다. HTML 문서에서 모델링 된 트리와 유사한 구조입니다. DOM은 DOM 구조 또는 특정 Elements 또는 Nodes를 상호 작용하고 수정하는 데 사용됩니다.

이와 같은 HTML 구조로 되어 있다고 상상해보십시오.

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>Document Object Model</title>
</head>

<body>
   <div>
      <p>
         <span></span>
      </p>
      <label></label>
      <input>
   </div>
</body>

</html>

DOM은 다음과 같을 것입니다. DOM

JavaScript의 document 객체는 DOM을 나타냅니다. 이것은 element 내용을 업데이트하기 위해 elements를 선택하는 데 사용할 수 있는 많은 방법을 제공합니다.

6. 이벤트 전파(Event Propagation) 란 무엇인가?

↑ 이벤트가 DOM element에서 발생하면 그 이벤트는 하나의 element에서만 발생하지 않습니다. 버블링 단계에서는 이벤트가 버블링되거나 부모, 조부모, 조부모의 부모에게, window에 도달할 때까지 진행되며 반면에 캡처링 단계에서는 이벤트가 window부터 이벤트를 트리거 한 element 또는 event.target까지 아래로 시작됩니다.

이벤트 전파는 3단계를 가집니다.

  1. Capturing Phase – 이벤트는 window에서 시작하여 대상 element에 도달할 때까지 모든 element를 내려갑니다.
  2. Target Phase – 이벤트가 대상 element에 도달합니다.
  3. Bubbling Phase – 대상 element에서 이벤트가 발생하고 window에 도달할 때까지 모든 element를 올라갑니다.

Event Propagation

7. 이벤트 버블링(Event Bubbling) 이란 무엇인가?

↑ 이벤트가 DOM element에서 발생하면 그 이벤트는 하나의 element에서만 발생하지 않습니다. 버블링 단계에서는 이벤트가 버블링되거나 부모, 조부모, 조부모의 부모에게, window에 도달할 때까지 진행됩니다.

이와 같은 마크업 예제가 있다면.

<div class="grandparent">
 <div class="parent">
   <div class="child">1</div>
 </div>
</div>

그리고 JS 코드.

function addEvent(el, event, callback, isCapture = false) {
 if (!el || !event || !callback || typeof callback !== 'function') return;
 if (typeof el === 'string') {
   el = document.querySelector(el);
 };
 el.addEventListener(event, callback, isCapture);
}

addEvent(document, 'DOMContentLoaded', () => {
 const child = document.querySelector('.child');
 const parent = document.querySelector('.parent');
 const grandparent = document.querySelector('.grandparent');

 addEvent(child, 'click', function (e) {
   console.log('child');
 });

 addEvent(parent, 'click', function (e) {
   console.log('parent');
 });

 addEvent(grandparent, 'click', function (e) {
   console.log('grandparent');
 });

 addEvent(document, 'click', function (e) {
   console.log('document');
 });

 addEvent('html', 'click', function (e) {
   console.log('html');
 });

 addEvent(window, 'click', function (e) {
   console.log('window');
 });
});

addEventListener 메서드에는 기본값이 false인 세 번째 옵션 매개 변수 useCapture가 있으며, 이 이벤트는 버블링 단계에서 발생하고 만약 true인 경우에는 캡쳐링 단계에서 발생합니다. child element를 클릭하면 콘솔에 child, parent, grandparent, html, document 및 window가 각각 로그로 기록됩니다. 이것이 이벤트 버블링입니다.

8. 이벤트 캡쳐링(Event Capturing) 이란 무엇인가?

↑ 이벤트가 DOM element에서 발생하면 그 이벤트는 하나의 element에서만 발생하지 않습니다. 캡처링 단계에서 이벤트는 window에서 이벤트를 트리거 한 element로 시작합니다.

이와 같은 마크업 예제가 있다면.

<div class="grandparent">
 <div class="parent">
   <div class="child">1</div>
 </div>
</div>

그리고 JS 코드.

function addEvent(el, event, callback, isCapture = false) {
 if (!el || !event || !callback || typeof callback !== 'function') return;
 if (typeof el === 'string') {
   el = document.querySelector(el);
 };
 el.addEventListener(event, callback, isCapture);
}

addEvent(document, 'DOMContentLoaded', () => {
 const child = document.querySelector('.child');
 const parent = document.querySelector('.parent');
 const grandparent = document.querySelector('.grandparent');

 addEvent(child, 'click', function (e) {
   console.log('child');
 }, true);

 addEvent(parent, 'click', function (e) {
   console.log('parent');
 }, true);

 addEvent(grandparent, 'click', function (e) {
   console.log('grandparent');
 }, true);

 addEvent(document, 'click', function (e) {
   console.log('document');
 }, true);

 addEvent('html', 'click', function (e) {
   console.log('html');
 }, true);

 addEvent(window, 'click', function (e) {
   console.log('window');
 }, true);
});

addEventListener 메서드에는 기본값이 false인 세 번째 옵션 매개 변수 useCapture가 있으며, 이 이벤트는 버블링 단계에서 발생하고 만약 true인 경우에는 캡쳐링 단계에서 발생합니다. child element를 클릭하면 콘솔에 window, document, html, grandparent, parent 및 child가 각각 로그로 기록됩니다. 이것이 이벤트 캡쳐링입니다.

9. event.preventDefault()와 event.stopPropagation() 메서드의 차이점은 무엇인가?

↑ event.preventDefault() 메서드는 element의 기본 동작을 방지합니다. form element에 사용하면 submitting 할 수 없습니다. anchor element에 사용하면 navigating 할 수 없습니다. contextmenu에서 사용하면 보여지거나 표시되지 않습니다. 반면에 event.stopPropagation() 메서드는 이벤트 전파를 중지하거나 버블링 또는 캡처링 단계에서 이벤트 발생을 중지합니다.

10. event.preventDefault() 메서드가 element에 사용되었는지 어떻게 확인하는가?

↑ 이벤트 객체에서 event.defaultPrevented 속성을 사용할 수 있습니다. 이것은 event.preventDefault()가 특정 element에서 호출되었는지 여부를 나타내는 boolean을 리턴합니다.

11. 이 코드에서 obj.someprop.x가 에러 발생하는 이유는 무엇인가?

const obj = {};
console.log(obj.someprop.x);

↑ 분명하게도, 이 에러는 undefined 값을 가진 someprop 속성에서 x 속성에 접근하려는 이유 때문에 발생합니다. 그 자체로 존재하지 않는 객체의 속성을 명심해야 되고 그리고 그것의 프로토 타입은 undefined를 기본값으로 가지며 undefined는 x의 속성이 없습니다.

12. event.target 이란 무엇인가?

↑ 간단히 말해서 event.target은 이벤트가 발생한 element 또는 이벤트를 트리거 한 element입니다.

샘플 HTML 마크업.

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
 <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
   <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
     <button style="margin:10px">
       Button
     </button>
   </div>
 </div>
</div>

샘플 JavaScript.

function clickFunc(event) {
 console.log(event.target);
}

버튼을 클릭하면 버튼 마크업이 로그로 기록됩니다. 이벤트를 가장 바깥쪽 div에 연결해도 버튼은 항상 기록됩니다. 그래서 event.target이 이벤트를 트리거 한 element라는 결론을 내릴 수 있습니다.

13. event.currentTarget 이란 무엇인가?

↑ event.currentTarget은 이벤트 핸들러를 명시적으로 붙이는 element입니다.

질문 12의 마크업 복사. 샘플 HTML 마크업.

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
 <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
   <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
     <button style="margin:10px">
       Button
     </button>
   </div>
 </div>
</div>

그리고 JS를 약간 변경.

function clickFunc(event) {
  console.log(event.currentTarget);
}

버튼을 클릭하더라도 가장 바깥 쪽 div 마크업이 로그로 기록됩니다. 이 예제에서 event.currentTarget이 이벤트 핸들러를 붙이는 element라고 결론을 내릴 수 있습니다.

14. == 와 === 의 차이는 무엇인가?

↑ == **(추상적 같음)**와 === **(엄격한 같음)**의 차이점은 == 는 강제변환(coercion) 후 값으로 비교하고 ===는 강제변환 없이 값 및 타입으로 비교한다는 것입니다.

== 에 대해 더 깊이 파봅시다. 그럼 먼저 강제변환에 대해 이야기합시다.

강제변환은 값을 다른 타입으로 변환하는 프로세스입니다. 이 경우와 같이 ==는 암묵적인 강제변환을 수행합니다. ==는 두 값을 비교하기 전에 수행해야 할 몇가지 조건이 있습니다.

x == y 값을 비교해야 한다고 가정해봅시다.

  1. x와 y가 같은 타입일 경우 === 연산자와 비교.
  2. x가 null이고 y가 undefined이면 true를 리턴.
  3. x가 undefined이고 y가 null이면 true를 리턴.
  4. x 타입이 number이고 y 타입이 string이면 x == toNumber(y)를 리턴.
  5. x 타입이 string이고 y 타입이 number이면 toNumber(x) == y를 리턴.
  6. x 타입이 boolean이면 toNumber(x) == y를 리턴.
  7. y 타입이 boolean이면 x == toNumber(y)를 리턴.
  8. x가 string, symbol 또는 number 중 하나이고 y 타입이 object이면 x == toPrimitive(y)를 리턴.
  9. x가 object이고 y가 string, symbol 중 하나이면 toPrimitive(x) == y를 리턴.
  10. false를 리턴.

Note: toPrimitive는 먼저 valueOf 메서드를 사용한 다음 객체의 toString 메서드를 사용하여 해당 객체의 원시값을 가져옵니다.

예를 들어 봅시다.

xyx == y
55true
1'1'true
nullundefinedtrue
0falsetrue
'1, 2'[1, 2]true
'[object Object]'{}true

이 예제들은 모두 true를 리턴합니다.

첫 번째 예는 x와 y의 타입과 값이 같으므로 조건 1에 해당합니다.

두 번째 예는 y는 비교하기 전에 number로 변환됨으로 조건 4에 해당합니다.

세 번째 예는 조건 2에 해당합니다.

네 번째 예는 y가 boolean이므로 조건 7로 해당합니다.

다섯 번째 예는 조건 8에 해당합니다. 배열은 1,2를 리턴하는 toString() 메서드를 사용하여 string으로 변환됩니다.

마지막 예는 조건 10에 해당합니다. 객체는 [object Object]를 리턴하는 toString() 메서드를 사용하여 string으로 변환됩니다.

xyx === y
55true
1'1'false
nullundefinedfalse
0falsefalse
'1,2'[1,2]false
'[object Object]'{}false

=== 연산자를 사용하면 첫 번째 예제를 제외한 모든 비교는 같은 타입이 아니기 때문에 false를 리턴합니다. 반면에 첫 번째 예제는 동일한 타입과 값을 가지기 때문에 true를 리턴합니다.

15. 자바스크립트에서 비슷한 두 객체를 비교할때 false를 리턴하는 이유는 무엇인가?

↑ 아래의 예가 있다고 가정해봅시다.

let a = { a: 1 };
let b = { a: 1 };
let c = a;

console.log(a === b); // 속성이 같더라도 로그는 false를 기록합니다.
console.log(a === c); // 로그 true hmm

JavaScript는 *객체(objects)*와 *원시값(primitives)*을 다르게 비교합니다. 원시값에서는 **값(value)**별로 비교하지만 객체에서는 참조 또는 변수가 저장된 메모리의 주소로 비교합니다. 그렇기에 첫 번째 console.log 문은false를, 두 번째console.log 문은true를 라턴한 이유입니다. a와c는 같은 참조를 가지고 있으며 a와b는 동일하지 않습니다.

16. !! 연산자는 무엇을 하는가?

↑ 이중 NOT 연산자 또는 !!는 오른쪽의 값을 boolean로 강제합니다. 기본적으로 값을 boolean로 변환하는 멋진 방법입니다.

console.log(!!null); //logs false
console.log(!!undefined); //logs false
console.log(!!''); //logs false
console.log(!!0); //logs false
console.log(!!NaN); //logs false
console.log(!!' '); //logs true
console.log(!!{}); //logs true
console.log(!![]); //logs true
console.log(!!1); //logs true
console.log(!![].length); //logs false

17. 한 줄에 여러 표현식을 평가하는 방법은 무엇인가?

↑ , 또는 쉼표 연산자를 사용하여 한 줄에서 여러 표현식을 평가할 수 있습니다. 왼쪽에서 오른쪽으로 평가하여 오른쪽 또는 마지막 피연산자의 마지막 항목 값을 리턴합니다.

let x = 5;

x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10);

function addFive(num) {
return num + 5;
}
console.log(x) // 27

x의 값을 로그로 기록하면 27이 됩니다. 먼저 x의 값을 6으로 증가시킨 다음 addFive(6) 함수를 호출합니다. 6을 매개 변수로 전달하고 그 결과를 x에 할당하면 x의 새로운 값은 11됩니다. 그런 다음 x의 현재 값을 2로 곱하면 x에 업데이트 된 x의 값은 22가 됩니다. 그 뒤 x의 현재 값에 5를 뺍니다. 그럼 그 결과를 x에 할당하면 업데이트 된 값은 17이 됩니다. 마지막으로 x의 값을 10 증가시킵니다. 그리고 x에 업데이트 된 값을 할당하면 x의 값은 27이 됩니다.