JavaScript 질문
프론트엔드 면접 질문 - JS 질문에 대한 해설입니다. Pull Request를 통한 제안, 수정 요청 환영합니다.
- 이벤트 위임에 대해 설명하세요.
this가 JavaScript에서 어떻게 작동하는지 설명하세요.- 프로토타입 상속이 어떻게 작동하는지 설명하세요.
- AMD vs CommonJS에 대해 어떻게 생각하나요?
- 다음이 IIFE로 작동하지 않는 이유를 설명하세요:
function foo(){ }();를 IIFE로 만들기 위해서는 무엇을 바꿔야하나요? null,undefined,undeclared의 차이점은 무엇인가요? 어떻게 이 상태들에 대한 확인을 할 것인가요?- 클로저는 무엇이며, 어떻게/왜 사용하나요?
.forEach루프와.map()루프 사이의 주요 차이점을 설명할 수 있나요? 왜 둘 중 하나를 선택할 것인가요?- 익명 함수의 일반적인 사용 사례는 무엇인가요?
- 코드를 어떻게 구성하나요? (모듈 패턴, 고전적인 상속?)
- 호스트 객체와 내장 객체의 차이점은 무엇인가요?
function Person(){},var person = Person(),var person = new Person()의 차이점은 무엇인가요?.call과.apply의 차이점은 무엇인가요?Function.prototype.bind에 대해 설명하세요.- 언제
document.write()를 사용하나요? - Feature detection, Feature inference, UA String의 차이점은 무엇인가요?
- Ajax에 대해 가능한 한 자세히 설명하세요.
- Ajax를 사용하는 것의 장단점은 무엇인가요?
- JSONP가 어떻게 동작하는지(그리고 Ajax와 어떻게 다른지)를 설명하세요.
- JavaScript 템플릿을 사용한 적이 있나요? 사용해봤다면, 어떤 라이브러리를 사용했나요?
호이스팅에 대해 설명하세요.- event bubbling에 대해 설명하세요.
- "attribute"와 "property"의 차이점은 무엇인가요?
- 내장 JavaScript 객체를 확장하는 것이 좋은 생각이 아닌 이유는 무엇인가요?
- document
load이벤트와 documentDOMContentLoaded이벤트의 차이점은 무엇인가요? ==와===의 차이점은 무엇인가요?- JavaScript와 관련하여 same-origin 정책을 설명하세요.
- 다음이 작동하게 만들어보세요.
- 왜 Ternary expression이라고 부르고, "Ternary"라는 단어는 무엇을 나타내나요?
"use strict";이 무엇인가요? 사용시 장단점이 무엇인가요?- 100까지 증가하면서
3의 배수에는fizz를 출력하고,5의 배수에는buzz를 출력하고,3과5의 배수에는fizzbuzz를 출력하는 for loop를 만드세요. - 일반적으로 웹 사이트의 전역 스코프를 그대로 두고 건드리지 않는 것이 좋은 이유는 무엇인가요?
- 왜
load이벤트와 같은 것을 사용하나요? 이 이벤트에는 단점이 있나요? 다른 대안을 알고 있나요? 알고 있다면 왜 그것을 사용할 건가요? - single page app이 무엇인지 설명하고 SEO-friendly하게 만드는 방법을 설명하세요.
- Promises와 그 Polyfill에 대한 당신의 경험은 어느 정도인가요?
- Callback 대신에 Promise를 사용할 때의 장점과 단점은 무엇인가요?
- JavaScript로 컴파일되는 언어로 JavaScript 코드를 작성하는 경우의 장단점은 무엇인가요?
- JavaScript 코드를 디버깅하기 위해 어떤 도구와 기술을 사용하나요?
- 오브젝트 속성이나 배열 항목을 반복할 때 사용하는 언어 구문은 무엇인가요?
- mutable 객체와 immutable 객체 사이의 차이점을 설명하세요.
- 동기, 비동기 함수의 차이점을 설명하세요.
- 이벤트 루프란 무엇인가요? 콜 스택과 태스크 큐의 차이점은 무엇인가요?
function foo() {}와var foo = function() {}사이에서foo사용의 차이에 대해 설명하세요.let,var,const를 사용하여 생성된 변수들의 차이점은 무엇인가요?- ES6 클래스와 ES5 함수 생성자의 차이점은 무엇인가요?
- 새 화살표 => 함수 문법에 대한 사용 예 시를 들 수 있나요? 이 새로운 문법은 다른 함수와 어떻게 다른가요?
- 생성자의 메서드에 화살표 문법을 사용하면 어떤 이점이 있나요?
- 고차 함수(higher-order function)의 정의는 무엇인가요?
- 객체나 배열에 대한 디스트럭쳐링 예시를 들 수 있나요?
- ES6 템플릿 리터럴은 문자열을 생성하는데 많은 유연성을 제공합니다. 이에 대한 예를 들 수 있나요?
- curry 함수의 예를 들어 줄 수 있나요? 그리고 이 문법은 어떤 이점을 가지고 있나요?
- spread 문법을 사용할 때의 이점은 무엇이며 rest 문법과 다른 점은 무엇인가요?
- 파일 간에 코드를 공유하려면 어떻게 해야하나요?
- 정적 클래스 멤버를 만드는 이유는 무엇인가요?
- 다른 답변들
이벤트 위임에 대해 설명하세요.
이벤트 위임은 이벤트 리스너를 하위 요소에 추가하는 대신 상위 요소에 추가하는 기법입니다. 리스너는 DOM의 event bubbling으로 인해 하위 요소에서 이벤트가 발생될 때마다 실행됩니다. 이 기술의 이점은 다음과 같습니다.
- 각 하위 항목에 이벤트 핸들러를 연결하지 않고, 상위 요소에 하나의 단일 핸들러만 필요하기 때문에 메모리 사용 공간이 줄어듭니다.
- 제거된 요소에서 핸들러를 해제하고 새 요소에 대해 이벤트를 바인딩할 필요가 없습니다.
참고자료
- https://davidwalsh.name/event-delegate
- https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation
this가 JavaScript에서 어떻게 작동하는지 설명하세요.
this는 간단하게 설명하기 어렵습니다. JavaScript에서 가장 혼란스러운 개념 중 하나입니다. 대략 설명하면 this의 값은 함수가 호출되는 방식에 따라 달라집니다. 온라인에 많은 설명을 읽었는데, Arnav Aggrawal의 설명이 가장 명확했습니다. 다음 규칙과 같습니다.
- 함수를 호출할 때
new키워드를 사용하는 경우, 함수 내부에 있는this는 완전히 새로운 객체입니다. apply,call,bind가 함수의 호출/생성에 사용되는 경우, 함수 내의this는 인수로 전달된 객체입니다.obj.method()와 같이 함수를 메서드로 호출하는 경우,this는 함수가 프로퍼티인 객체입니다.- 함수가 자유함수로 호출되는 경우, 즉, 위의 조건 없이 호출되는 경우
this는 전역 객체입니다. 브라우저에서는window객체입니다. 엄격 모드('use strict') 일 경우,this는 전역 객체 대신undefined가 됩니다. - 위의 규칙 중 다수가 적용되면 더 상위 규칙이 승리하고
this값을 설정합니다. - 함수가 ES2015 화살표 함수인 경우 위의 모든 규칙을 무시하고 생성된 시점에서 주변 스코프의
this값을 받습니다.
상세한 설명은 Medium의 글을 참조하세요.
References
- https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3
- https://stackoverflow.com/a/3127440/1751946
프로토타입 상속이 어떻게 작동하는지 설명하세요.
이는 매우 일반적인 JavaScript 인터뷰 질문입니다. 모든 JavaScript 객체는 다른 객체에 대한 참조인 __proto__ 프로퍼티를 가지고 있습니다. 객체의 프로퍼티에 접근할 때, 해당 객체에 해당 프로퍼티가 없으면 JavaScript 엔진은 객체의 __proto__과 __proto__의 __proto__등을 보고 프로퍼티 정의가 있을 때까지 찾고, 만약 객체의 프로퍼티에 접근할 때 해당 객체에 해당 프로퍼티가 없으면 프로토타입 체인 중 하나에 있거나 프로토타입 체인의 끝에 도달할 때까지 찾습니다. 이 동작은 고전적인 상속을 흉내내지만, 실제로 상속보다 위임에 더 가깝습니다.
참고자료
- https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson
- https://davidwalsh.name/javascript-objects
AMD vs CommonJS에 대해 어떻게 생각하나요?
두 가지 모두 ES2015가 등장하기 전까지 JavaScript에 기본적으로 존재하지 않는 모듈 시스템을 구현하는 방법입니다. CommonJS는 동기식인 반면 AMD(Asynchronous Module Definition - 비동기식 모듈 정의)는 분명히 비동기식입니다. CommonJS는 서버사이드 개발을 염두에 두고 설계되었으며, AMD는 모듈의 비동기 로딩을 지원하므로 브라우저용으로 더 많이 사용됩니다.
AMD은 구문이 매우 장황하고, CommonJS은 다른 언어처럼 import 문을 작성하는 스타일에 더 가깝습니다. 대부분의 경우 AMD를 필요로 하지 않습니다. 모든 JavaScript를 연결된 하나의 번들 파일로 제공하면 비동기 로딩 속성의 이점을 누릴 수 없기 때문입니다. 또한 CommonJS 구문은 모듈 작성의 노드 스타일에 가깝고 클라이언트 사이드와 서버사이드 JavaScript 개발 사이를 전환할 때 문맥 전환 오버 헤드가 적습니다.
ES2015 모듈이 동기식 및 비동기식 로딩을 모두 지원하는 것이 반가운 것은 마침내 하나의 접근 방식만 고수할 수 있다는 점입니다. 브라우저와 노드에서 완전히 작동되지는 않지만, 언제나 트랜스파일러를 사용하여 코드를 변환할 수 있습니다.
참고자료
- https://auth0.com/blog/javascript-module-systems-showdown/
- https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs
다음이 IIFE로 작동하지 않는 이유를 설명하세요: function foo(){ }();를 IIFE로 만들기 위해서는 무엇을 바꿔야하나요?
IIFE는 즉시 함수 호출 표현식(Immediately Invoked Function Expressions)을 의미합니다. JavaScript parser는 function foo(){ }();을 function foo(){ }와 ();로 읽습니다. 전자는 함수 선언이며 후자(한 쌍의 중괄호)는 함수를 호출하려고 시도했지만, 이름이 지정되지 않았기 때문에 Uncaught SyntaxError : Unexpected token )을 발생시킵니다.
괄호를 추가하여 고치는 두 가지 방법이 있습니다: (function foo(){ })() 그리고 (function foo(){ }()). function으로 시작하는 문은 함수 선언으로 간주됩니다. 이 함수를 ()로 묶으면 함수 식이 되고, 이렇게하면 다음 ()로 함수를 실 행할 수 있습니다. 이러한 함수는 전역 범위에 노출되지 않으며, 본문 내에서 이 함수 자체를 참조할 필요가 없는 경우에는 해당 함수의 이름을 생략할 수도 있습니다.
void function foo(){ }();처럼 void 연산자를 사용할 수도 있습니다. 불행히도, 이러한 접근방식에는 한 가지 문제가 있습니다. 주어진 표현식의 평가는 항상 undefined이므로, IIFE 함수가 무언가를 반환하면, 사용할 수 없습니다. 예:
// Don't add JS syntax to this code block to prevent Prettier from formatting it.
const foo = void (function bar() {
return 'foo';
})();
console.log(foo); // undefined
참고자료
- http://lucybain.com/blog/2014/immediately-invoked-function-expression/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
null, undefined, undeclared의 차이점은 무엇인가요? 어떻게 이 상태들에 대한 확인을 할 것인가요?
Undeclared 변수는 이전에 var, let, const를 사용하여 생성되지 않은 식별자에 값을 할당할 때 생성됩니다. Undeclared 변수는 현재 범위 외부에 전역으로 정의됩니다. strict 모드에서는 Undeclared 변수에 할당하려고 할 때, ReferenceError가 발생합니다. Undeclared 변수는 전역 변수처럼 좋지 않습니다. 이것들은 모두 사용을 피하세요! 이를 검사하 려면, 사용할 때 try/catch 블록에 감싸세요.
function foo() {
x = 1; // strict 모드에서 ReferenceError를 발생시킵니다.
}
foo();
console.log(x); // 1
undefined 변수는 선언되었지만, 값이 할당되지 않은 변수입니다. 이는 undefined 타입입니다. 함수가 실행 결과에 따라 아무값도 반환하지 않으면, 변수에 할당되며, 그 변수가 undefined 값을 갖습니다. 이를 검사하기 위해, 엄격한 (===) 연산자 또는 typeof에 undefined 문자열을 사용하여 비교하세요. 확인을 위해 추상 평등 연산자(==)를 사용해서는 안되며, 이는 값이 null이면 true를 반환합니다.
var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true
console.log(foo == null); // true. 옳지않습니다. 이렇게 사용하지 마세요.
function bar() {}
var baz = bar();
console.log(baz); // undefined
null인 변수는 null 값이 명시적으로 할당된 것입니다. 그것은 값을 나타내지 않으며, 명시적으로 할당됐다는 점에서 undefined와 다릅니다. null을 체크하기 위해서 단순히 완전 항등 연산자(===)를 사용하여 비교하면 됩니다. 위와 같이, 추상 평등 연산자 (==)를 사용해서는 안되며, 값이 undefined이면 true를 반환합니다.
var foo = null;
console.log(foo === null); // true
console.log(foo == undefined); // true. 옳지않습니다. 이렇게 사용하지 마세요.
개인적 습관으로, 저는 변수를 선언하지 않거나(undeclared) 할당하지 않은 상태(unassigned)로 두지 않습니다. 아직 사용하지 않으려는 경우, 선언한 후에 명시적으로 null을 할당할 것입니다. 작업시 linter를 사용하면, 일반적으로 Undeclared 변수를 참조하지는 않는지 확인할 수 있습니다.
참고자료
- https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined
클로저는 무엇이며, 어떻게/왜 사용하나요?
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합입니다. "렉시컬"은 렉시컬 범위 지정이 변수가 사용 가능한 위치를 결정하기 위해 소스 코드 내에서 변수가 선언된 위치를 사용한다는 사실을 나타냅니다. 클로저는 외부 함수가 반환된 후에도 외부 함수의 변수 범위 체인에 접근할 수 있는 함수입니다.
왜 클로저를 사용합니까?
- 클로저로 데이터 프라이버시 / private method를 모방. 일반적으로 모듈 패턴에 사용됩니다.
- 부분 적용 또는 currying.
참고자료
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
- https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36
.forEach 루프와 .map() 루프 사이의 주요 차이점을 설명할 수 있나요? 왜 둘 중 하나를 선택할 것인가요?
이 두 함수의 차이점을 이해하기 위해 각 함수가 무엇을 하는지 살펴보겠습니다.
forEach
- 배열의 요소를 반복합니다.
- 각 요소에 대해 콜백을 실행합니다.
- 값을 반환하지 않습니다.
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
// num나 index로 무언가 합니다.
});
// doubled = undefined
map
- 배열의 요소를 반복합니다.
- 각 요소에서 함수를 호출하여 결과로 새 배열을 작성하여 각 요소를 새 요소에 매핑합니다.
const a = [1, 2, 3];
const doubled = a.map((num) => {
return num * 2;
});
// doubled = [2, 4, 6]
.forEach와 .map()의 가장 큰 차이점은 .map()이 새로운 배열을 반환한다는 것입니다. 결과가 필요하지만 원본 배열을 변경하고 싶지 않으면, .map()이 확실한 선택입니다. 단순히 배열을 반복할 필요가 있다면, forEach가 좋은 선택입니다.
참고자료
익명 함수의 일반적인 사용 사례는 무엇인가요?
익명함수는 IIFE로 사용되어 지역 범위 내에서 일부 코드를 캡슐화하므로 선언된 변수가 전역 범위로 누출되지 않습니다.
(function () {
// 코드
})();
한 번 사용되고 다른 곳에서는 사용할 필요가 없는 콜백으로 사용됩니다. 함수 본체를 찾기 위해 다른 곳을 찾아볼 필요 없이 코드를 호출하는 코드 바로 안에 핸들러가 정의되어 있으면 코드가 보다 독립적이고 읽기 쉽게 보일 것입니다.
setTimeout(function () {
console.log('Hello world!');
}, 1000);
함수형 프로그래밍 또는 Lodash에 대한 인수(콜백과 유사)로 사용.
const arr = [1, 2, 3];
const double = arr.map(function (el) {
return el * 2;
});
console.log(double); // [2, 4, 6]
참고자료
- https://www.quora.com/What-is-a-typical-usecase-for-anonymous-functions
- https://stackoverflow.com/questions/10273185/what-are-the-benefits-to-using-anonymous-functions-instead-of-named-functions-fo
코드를 어떻게 구성하나요? (모듈 패턴, 고전적인 상속?)
과거에는 Backbone 모델을 만들고 그 모델에 메소드를 연결하는 등 OOP 접근 방식을 장려하는 모델에 Backbone 을 사용했습니다.
모듈 패턴은 여전히 훌륭하지만, 요즘에는 React/Redux 기반의 Flux 아키텍처를 사용합니다. 이 아키텍처는 단방향 프로그래밍 방식을 권장합니다. 저는 평범한 객체를 사용하여 응용 프로그램의 모델을 표현하고 이러한 객체를 조작하는 유틸리티 순수 함수를 작성합니다. 상태는 다른 Redux 응용 프로그램에서와 마찬가지로 action 및 reducer를 사용하여 조작됩니다.
가능한 경우 고전적인 상속을 사용하지 않습니다. 저는 이 규칙들을 유지합니다.