[프론트엔드 인터뷰]FE-Study 1주차 1회

이벤트 위임

  • 이벤트 위임은 이벤트 리스너를 하위 요소에 추가하는 대신 상위 요소에 추가하는 기법입니다. 리스너는 DOM의 event bubbling으로 인해 하위 요소에서 이벤트가 발생될 때마다 실행됩니다. 이 기술의 이점은 다음과 같습니다.
  • 각 하위 항목에 이벤트 핸들러를 연결하지 않고, 상위 요소에 하나의 단일 핸들러만 필요하기 때문에 메모리 사용 공간이 줄어듭니다.
  • 제거된 요소에서 핸들러를 해체하고 새 요소에 대해 이벤트를 바인딩할 필요가 없습니다.
<ul id="parent-list">
  <li id="post-1">항목 1</li>
  <li id="post-2">항목 2</li>
  <li id="post-3">항목 3</li>
  <li id="post-4">항목 4</li>
  <li id="post-4">항목 4</li>
</ul>

각 li 요소를 클릭할 때 이벤트가 발생해야한다고 가정할 때 각 개별 li요소에 별도의 이벤트 리스너를 추가할 수 있지만 li요소가 추가되거나 삭제되면 번거롭게 우리는 새로 li에 이벤트를 추가하거나 삭제하는 작업을 해주어야한다. 이러한 문제를 해결하기 위해 우리는 ul요소에 이벤트 리스너를 추가할 수 있습니다.

document.getElementById('parent-list').addEventListener('click', function(e) {
  if (e.target && e.target.nodeName === 'Li') {
    console.log('List item', e.target.id, 'was clicked')
  }
})

this가 JavaScript에서 어떻게 작동하나요?

this의 값은 함수가 호출되는 방식에 따라 달라집니다.

1.함수를 호출할 때 new 키워드를 사용하는 경우, 함수 내부에 있는 this는 완전히 새로운 객체입니다.

function ConstructorExample() {
  console.log(this)
  this.value = 10
  console.log(this)
}

new ConstructorExample()

// => {}
// => {value: 10}

2.apply,call, bind가 함수의 호출/생성에 사용되는 경우, 함수 내의 this는 인수로 전달된 객체입니다.

function fn() {
  console.log(this)
}

var obj = {
  value: 5,
}

var boundFn = fn.bind(obj)

boundFn() // => {value: 5};
fn.call(obj) // => {value: 5};
fn.apply(obj) // => {value: 5};

3.obj.method()와 같이 함수를 메서드로 호출하는 경우, this는 함수가 프로퍼티인 객체입니다.

var obj = {
  value: 5,
  printThis: function() {
    console.log(this)
  },
}

obj.printThis() // => { value: 5, printThis: f }

4.함수가 자유함수로 호출되는 경우, 즉, 위의 조건 없이 호출되는 경우 this는 전역객체입니다. 브라우저에는 window 객체입니다. 엄격모드(“use strict”)일 경우, this는 전역 객체 대신 undefined가 됩니다.

function fn() {
  console.log(this)
}

fn() // Window

5.함수가 ES2015 화살표 함수인 경우 위의 모든 규칙을 무시하고 생성된 시점에서 주변 스코프의 this값을 받습니다.

const obj = {
    value: 'abc'
    createArrowFn: function() {
        return () => console.log(this);
    }
};

const arrowFn = obj.createArrowFn();

arrowFn(); // => { value: 'abc', createArrowFn: f }

프로토타입 상속이 어떻게 작동하는지 설명하세요.

모든 javaScript 객체는 다른 객체에 대한 참조인 proto 프로퍼티를 가지고 있습니다. 객체의 프로퍼티에 접근할 때, 해당 객체에 해당 프로퍼티가 없으면 JavaScript 엔진은 객체의 proto를 보고 프로퍼티 정의가 있을 때까지 찾고, 만약 객체의 프로퍼티에 접근할 때 해당 객체에 해당 프로퍼티가 없으면 프로토타입 체인중 하나에 있거나 프로토타입 체인의 끝에 도달할 때까지 찾습니다. 이동작은 고전적인 상속을 흉내내지만, 실제로 상속보다 위임에 더 가깝습니다.

obj객체에서 toString() 메서드를 호출했다. 선언된 메서드가 obj 객체 내에 없는데 어떻게 함수 호출이 성공했을까요?

const obj = {
  name: 'javascript',
}

console.log(obj.toString()) // '[Object object]'

이것은 프로토타입 체인이라는 개념 때문에 가능합니다. 프로토타입 체인은 상위 프로토타입과 연쇄적으로 연결된 구조를 의미합니다. 그리고 프로퍼티나 메서드에 접근하기 위해 이 연결 구조를 따라 차례대로 검색하는 것을 프로토타입 체이닝이라고 합니다. 예제 코드는 다음의 과정처럼 프로토타입 체인을 통해 toString() 메서드가 호출된 것입니다.

정리하자면 obj 객체의 toString() 메서드를 호출하기 위해 obj 객체의 프로퍼티나 메서드를 검색하고, 찾지못하면 프로토타입 체인을 통해 상위 프로토타입에서 toString() 메서드를 검색합니다. 그리고 상위 프로토타입에서 찾으면 그 메서드를 호출합니다.

CommonJS와 AMD

두 가지 모두 ES2015가 등장하기 전까지 JavaScript에 기본적으로 존재하지 않는 모듈 시스템을 구현하는 방법입니다.

CommonJS & AMD는 js 애플리케이션에서 어떻게 모듈과 dependency가 선언되는지를 정의하는 명세로, commonJS의 동기적 모듈 호출에 따르는 브라우저단의 모듈링에서 이슈를 해결하기 위해 commomsJS로부터 파생된 모듈 스펙인 AMD(병렬 모듈 호출)이 등장했습니다. 기본적 결정적인 차이는 CommonJS는 동기식인 반면, AMD는 분명히 비동기식 이라는 점 입니다.

일반적으로 client에서는 AMD, server에서는 commonJS가 적합하지만 두 스펙 모두 server, client 환경에서 모두 사용이 가능합니다.

CommonJS를 따르는 대표적인 프로젝트는 Node.js가 있고, AMD를 따르는 대표적인 프로젝트는 RequireJS, curl.js등이 있다.

ES2015 모듈이 동기식 및 비동기식 로딩을 모두 지원하는 것이 반가운 것은 마침내 하나의 접근 방식만 고수할 수 있다는 점입니다. 브라우저와 노드에서 완전히 작동되지는 않지만, 언제나 트랜스파일러를 사용하여 코드를 변환할 수 있습니다.

우리가 흔히 사용하는 require, module.exports가 CommonJs 모듈방식이고, import, export는 ES6이후로 출시된 모듈 방식입니다.

function foo() { } 를 IIFE로 만들기 위해서는 무엇을 바꿔야하나요?

IIFE는 즉시 함수 호출 표현식을 의미합니다. function foo(){}();을 function foo(){}와 ();로 읽습니다. 전자는 함수 선언이며 후자는 함수를 호출하려고 시도했지만 이름이 지정되지 않았기 때문에 Uncaught SyntaxError : Unexpected token )을 발생시킵니다.

괄호를 추가하여 고치는 두가지 방법이 있습니다. 아래와 같이 즉시 실행 함수 선언문으로 작성하면 전역 범위에 노출되지 않으며, 본문 내에서 이 함수 자체를 참조할 필요가 없는 경우에는 해당 함수의 이름을 생략할 수도 있습니다.

;(function foo() {})()

void function foo(){ }(); 처럼 void 연산자를 사용할 수도 있는데, 이런 방법은 항상 반환값으로 undefined이므로 함수가 무언가를 반환해야 할 때는 사용할 수 없습니다.

const foo = void (function bar() {
  return 'foo'
})()

console.log(foo) // undefined

const foo = (function bar() {
  return 'foo'
})()

console.log(foo) // foo