logo

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

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 🎆🎆🎆!

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

질문

18. 호이스팅(Hoisting)이란 무엇인가?

호이스팅은 변수나 함수를 정의하는 (글로벌 또는 함수) 스코프의 상단으로 변수함수가 이동하는 것을 설명하는데 사용되는 용어입니다.

호이스팅을 이해하려면 *실행 컨텍스트(execution context)*를 설명해야합니다.
실행 컨텍스트는 현재 실행중인 “코드 환경”입니다. 실행 컨텍스트에는 컴파일(compilation) 및 *실행(execution)*의 두 단계가 있습니다.

컴파일 - 이 단계에서는 모든 함수 선언호이스트를 스코프의 맨 위로 올려서 우리가 나중에 그것들을 참조할 수 있고 모든 변수 선언**(var 키워드로 선언)**도 얻을 수 있으며 또한 호이스트를 올려서 그들에게 기본값으로 undefined를 부여합니다.

실행 - 이 단계에서는 앞서 호스팅 된 변수에 값을 할당하고 함수 **(객체의 메서드)**를 실행하거나 호출합니다.

Note: var 키워드로 선언된 함수 선언과 변수만 호이스팅 된다. 함수 표현식이나 화살표 함수, letconst 키워드가 안된다.

Ok, 아래의 글로벌 스코프에 예제 코드가 있다고 가정해 봅시다.

console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));

function greet(name){
  return 'Hello ' + name + '!';
}

var y;

이 코드는 각각 undefined, 1, Hello Mark!를 로그를 기록합니다.

컴파일 단계는 다음과 같습니다.

function greet(name) {
  return 'Hello ' + name + '!';
}

var y; // 암묵적 "undefined" 할당

// 마무리를 위한 "컴파일" 단계를 기다리는

// 그리고 "실행"단계를 시작합니다

/*
console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));
*/

이 예제는, 변수 및 함수 호출할당에 대한 코멘트입니다.

컴파일 단계가 완료된 후 메서드 호출을 하는 실행 단계를 시작하고 값을 변수에 지정합니다.

function greet(name) {
  return 'Hello ' + name + '!';
}

var y;

// "실행"단계 시작

console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));

19. 스코프(Scope)란 무엇인가?

JavaScript의 스코프는 변수 또는 함수에 대한 유효한 액세스 권한이 있는 영역입니다. JavaScript는 3가지 스코프를 가집니다. 글로벌 스코프, 함수 스코프 그리고 블럭 스코프(ES6).

  • 글로벌 스코프 - 글로벌 네임 스페이스에 선언된 변수 또는 함수는 글로벌 스코프에 있으므로 코드의 모든 곳에서 액세스 할 수 있습니다.
//global namespace
var g = "global";

function globalFunc(){
  function innerFunc(){
    console.log(g); // "g"는 글로벌 변수이므로 "g"에 액세스 할 수 있습니다.
  }
  innerFunc();
}
  • 함수 스코프 - 함수 내에 선언된 변수, 함수 및 매개 변수는 해당 함수 내부에서 액세스 할 수 있지만 외부에서는 액세스 할 수 없습니다.
function myFavoriteFunc(a) {
  if (true) {
    var b = "Hello " + a;
  }
  return b;
}
myFavoriteFunc("World");
console.log(a); // "a"가 정의되어 있지 않아서 ReferenceError를 던집니다.
console.log(b); // 에러로 계속되지 않습니다.

블럭 스코프 - {} 블록 안에 선언된 변수 (let, const) 는 그 안에서만 액세스 할 수 있습니다.

function testBlock() {
  if(true){
    let z = 5;
  }
  return z;
}

testBlock(); // "z"가 정의되어 있지 않아서 ReferenceError를 던집니다.

스코프는 변수를 찾기 위한 일련의 규칙입니다. 현재 스코프에 변수가 없으면 외부 스코프에서 변수를 찾고 검색합니다. 그리고 다시 존재하지 않으면 글로벌 스코프에 닿을 때까지 다시 찾습니다. 만약 그 변수가 존재하면 그것을 사용합니다. 존재하지 않으면 에러를 던집니다. 가장 가까운 변수를 검색하고 검색을 중지하거나 다시 찾습니다. 이것을 스코프 체인이라고 합니다.

/* Scope Chain
Inside inner function perspective
inner's scope -> outer's scope -> global's scope
*/

// Global Scope
var variable1 = "Comrades";
var variable2 = "Sayonara";

function outer(){
// outer's scope
  var variable1 = "World";
  function inner(){
  // inner's scope
    var variable2 = "Hello";
    console.log(variable2 + " " + variable1);
  }
  inner();
}
outer();
// logs Hello World
// (variable2 = "Hello")와 (variable1 = "World")가 inner's scope 안 변수에 가까이 있기 때문입니다.

Scope

20. 클로저(Closures)란 무엇인가?

클로저는 논란의 여지가 많은 주제이므로 아마도 모든 질문 중에서 가장 어려운 질문일 것입니다. 그래서 제가 이해한 내용으로 설명하겠습니다.

클로저는 간단하게 말해서 스코프 체인의 도움으로 글로벌 스코프에 도달할 때까지 현재 스코프, 그것의 부모 함수 스코프, 또 그것의 부모의 부모 함수 스코프에서 변수와 매개 변수의 참조를 기억하는 기능입니다. 기본적으로 클로저는 함수가 선언될 때 생성된 스코프입니다.

이 예제는 클로저를 설명하는 좋은 방법입니다.

// Global's Scope
var globalVar = "abc";
function a() {
// testClosures's Scope
  console.log(globalVar);
}
a(); //logs "abc"
/* Scope Chain
   내부 함수의 시점
   a의 스코프 -> 글로벌 스코프
*/

이 예제에서 a 함수를 선언할때 글로벌 스코프a클로저 일부입니다.

Closures1

위 이미지에서 값이 없는 변수 globalVar가 그 이유로 a 함수를 호출하는 위치시간에 따라 그 변수의 값이 변경될 수 있기 때문입니다.

그러나 위의 예제에서 globalVar 변수의 값은 abc입니다.

Ok, 복잡한 예를 들어 봅시다.

var globalVar = "global";
var outerVar = "outer";

function outerFunc(outerParam) {
  function innerFunc(innerParam) {
    console.log(globalVar, outerParam, innerParam);
  }
  return innerFunc;
}

const x = outerFunc(outerVar);
outerVar = "outer-2";
globalVar = "guess";
x("inner");

Closures2

이것은 “guess outer inner”를 로그를 기록합니다. 이에 대한 설명으로 outerFunc 함수를 호출하고 리턴된 값을 변수 xinnerFunc 함수 할당할 때, outerParamouterVar 변수에 새로운 outer-2 값을 할당하더라도 outer 값을 갖습니다. 왜냐하면 outerFunc을 호출한 후 재할당이 발생했기 때문에 그리고 이때 outerFunc 함수를 호출하면 스코프 체인에서 outerVar의 값을 찾게 되며 outerVar의 값은 **“outer”**입니다.

이제, innerFunc에 대한 참조가 있는 x 변수를 호출하면, innerParaminner값을 가지게 될 것입니다. 왜냐하면 그 값은 호출 할 때 전달하는 값이고 x 변수 호출하기 전에 globalVar에 새 값을 할당하기 때문에 globalVar 변수는 guess 값을 가지게 될 것입니다. 그리고 호출 할 때 스코프 체인에 있는 globalVarx 값은 guess입니다.

클로저를 올바르게 이해하지 못하는 문제를 보여주는 예가 있습니다.

const arrFuncs = [];
for(var i = 0; i < 5; i++) {
  arrFuncs.push(function() {
    return i;
  });
}
console.log(i); // i is 5

for (let i = 0; i < arrFuncs.length; i++) {
  console.log(arrFuncs[i]()); // all logs "5"
}

이 코드는 클로저로 인해 예상대로 작동하지 않습니다. var키워드는 글로벌 변수를 만들고 함수를 푸시할때 글로벌 변수 i를 리턴합니다. 그래서 루프 후에 해당 배열에서 관련 함수 중 하나를 호출하면 현재값이 5이고 글로벌 변수라 접근이 가능한 i를 얻을 수 있기 때문에 5로 로그를 기록합니다. 클로저는 생성 시 해당 변수의 이 아닌 해당 변수의 참조를 유지하기 때문입니다.

IIFES를 사용하거나 블록 스코프 지정을 위해 var키워드를 let으로 변경하여 이것을 해결할 수 있습니다.

21. 자바스크립트에서 falsy란 무엇인가?

const falsyValues = ['', 0, null, undefined, NaN, false];

falsy 값은 부울로 변환 될 때 false가 되는 값입니다.

22. falsy값을 어떻게 확인할 수 있는가?

부울(Boolean) 함수 또는 Double NOT 연산자 !!를 사용하면 됩니다.

23. use strict은 무엇을 하는가?

"use strict"은 함수 또는 전체 스크립트에서 **엄격한 모드(Strict Mode)**로 코드를 작성하는 JavaScript의 ES5 기능입니다. 엄격한 모드는 코드 초기에 버그를 피하고 이에 대한 제한을 추가합니다.

엄격한 모드가 제공하는 제한 사항.

  • 선언되지 않은 변수 할당 또는 액세스.
function returnY() {
  "use strict";
  y = 123;
  return y;
}
  • 읽기 전용 또는 쓰기 불가능 글로벌 변수에 값을 할당.
"use strict";
var NaN = NaN;
var undefined = undefined;
var Infinity = "and beyond";
  • 삭제 불가능한 속성을 삭제.
"use strict";
const obj = {};
Object.defineProperty(obj, 'x', {
   value : '1'
});
// obj.x = 1 과는 다른 선언
// https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
delete obj.x;
  • 매개 변수 이름이 중복.
"use strict";
function someFunc(a, b, b, c){
}
  • eval 함수를 사용하여 변수 만들기
"use strict";
eval("var x = 1;");
console.log(x);
// x가 정의되지 않았다는 참조 오류를 던짐.

this의 기본값은 undefined가 됨.

"use strict";

function showMeThis(){
  return this;
}

showMeThis(); //returns undefined

위의 모든 예제들은 오류를 던집니다. 엄격한 모드에는 이러한 것보다 더 많은 제한이 있습니다.

24. 자바스크립트에서 this 값은 무엇인가?

기본적으로 this는 현재 함수를 실행하거나 호출하는 객체의 값을 말합니다. 나는 현재 우리가 그것을 사용하는 상황과 장소에 따라 this의 값은 변하기 때문이라 말하고 있습니다.

const carDetails = {
  name: "Ford Mustang",
  yearBought: 2005,
  getName() {
     return this.name;
  },
  isRegistered: true
};
console.log(carDetails.getName()); // logs Ford Mustang

이것은 getName 메서드에서 this.name을 리턴하기 때문에 일반적으로 예상할 수 있는 것입니다. 이 문맥상의 코드에서 this는 현재 함수 실행의 객체 “소유자” carDetails 객체를 참조합니다.

Ok, 이상해지도록 코드를 추가해 봅시다. 아래의 console.log문에 세 줄의 코드를 추가.

var name = "Ford Ranger";
var getCarName = carDetails.getName;

console.log(getCarName()); // logs Ford Ranger

두 번째 console.log문은 Ford Ranger라는 단어를 프린트하는데, 이것은 첫 번째 console.log문에서 Ford Mustang을 프린트했기 때문에 이상합니다. 그 이유는 getCarName메서드가 window 객체와 다른 “소유자” 객체를 가지고 있기 때문입니다. 글로벌 스코프에서 var 키워드로 변수를 선언하면 변수와 이름이 같은 window 객체의 속성이 첨부됩니다. 글로벌 스코프에서 this"use strict"가 사용되지 않을 때 window 객체를 나타내는 것을 명심해야 합니다.

console.log(getCarName === window.getCarName); //logs true
console.log(getCarName === this.getCarName); // logs true

위 예제에서 thiswindow는 같은 객체를 가리킵니다.

이 문제를 해결하는 한 가지 방법은 함수에 applycall 메서드를 사용하는 것입니다.

console.log(getCarName.apply(carDetails)); // logs Ford Mustang
console.log(getCarName.call(carDetails));  // logs Ford Mustang

applycall 메서드는 첫 번째 매개 변수가 해당 함수 내에서 this의 값이 되는 객체가 될 것으로 예상합니다.

IIFE 또는 즉시 실행 함수 표현식, 글로벌 스코프에서 선언된 함수, 익명 함수와 객체 안의 메서드에 있는 내부 함수에는 window 객체를 가리키는 this 기본값을 가집니다.

(function (){
  console.log(this);
})(); // logs the "window" object

function iHateThis(){
   console.log(this);
}

iHateThis(); // logs the "window" object

const myFavoriteObj = {
  guessThis() {
    function getName() {
      console.log(this.name);
    }
    getName();
  },
  name: 'Marko Polo',
  thisIsAnnoying(callback) {
    callback();
  }
};

myFavoriteObj.guessThis(); // logs the "window" object
myFavoriteObj.thisIsAnnoying(function () {
  console.log(this); // logs the "window" object
});

myFavoriteObj객체에서 Marko Polo 인 name 속성값을 얻으려면 두 가지 방법이 있습니다.

첫번째는 this의 값을 변수에 저장하는 것입니다.

const myFavoriteObj = {
  guessThis() {
    const self = this; // this 값을 "self"변수에 저장
    function getName() {
      console.log(self.name);
    }
    getName();
  },
  name: 'Marko Polo',
  thisIsAnnoying(callback) {
    callback();
  }
};

이 이미지에서 우리는 myFavoriteObj객체가 될 this의 값을 저장합니다. 그래서 getName 내부 함수 안을 접근할 수 있습니다.

두번째로, ES6 화살표 함수를 사용하는 것입니다.

const myFavoriteObj = {
  guessThis() {
    const getName = () => {
      // 이 화살표 함수의 외부 "this"값을 복사
      console.log(this.name);
    }
    getName();
  },
  name: 'Marko Polo',
  thisIsAnnoying(callback){
    callback();
  }
};

화살표 함수에는 자체 this가 없습니다. 이것은 렉시컬 스코프(lexical scope)를 포함하는 this 값 또는 이 예제에서는 myFavoriteObj객체가 될 내부 함수 getName의 외부 this 값을 복사합니다. 함수가 어떻게 호출되는지에 대한 this 값을 결정할 수도 있습니다.

25. 객체의 prototype은 무엇인가?

가장 간단한 용어로 prototype 은 객체의 청사진(blueprint)입니다. 현재 객체에 존재하는 경우 속성메서드의 폴백으로 사용됩니다. 이것은 객체간에 속성과 기능을 공유하는 방법입니다. JavaScript의 Prototypal Inheritance와 관련된 핵심 개념입니다.

const o = {};
console.log(o.toString()); // logs [object Object]

비록 o.toString 메서드가 o객체에 존재하지 않더라도 에러를 던지지 않고 [object Object]를 리턴합니다. 객체에 속성이 없으면 프로토 타입을 살펴 봅니다. 그리고 여전히 존재하지 않으면 프로토 타입의 프로토 타입을 살펴 봅니다. 프로토 타입 체인에서 동일한 속성을 찾을 때까지 계속됩니다. 프로토 타입 체인의 끝은 Object.prototype입니다.

console.log(o.toString === Object.prototype.toString); // logs true
// 우리가 프로토 타입 체인을 찾고 있음을 의미하고
// Object.prototype에 도달하여 "toString"메서드를 사용했습니다.

26. IIFE는 무엇이며, 그 사용법은 무엇인가?

IIFE 또는 즉시 실행 함수 표현식은 생성 또는 선언 후에 바로 호출되거나 실행되는 함수입니다. IIFE를 생성하는 구문은 function() {}을 괄호 () 또는 그룹 연산자로 감싸서 함수를 표현식으로 처리 후 다른 괄호()로 호출합니다. 따라서 IIFE는 (function() {})()처럼 생겼습니다.

(function () {
  //
}());

(function () {
  //
})();

(function named(params) {
  //
})();

(() => {
  //
});

(function (global) {
  //
})(window);

const utility = (function () {
   return {
      // utilities
   };
})

이 예제는 모두 유효한 IIFE입니다. 마지막에서 두 번째 예는 IIFE 함수에 인수를 전달할 수 있음을 보여줍니다. 마지막 예는 IIFE의 결과를 변수에 저장하여 나중에 참조할 수 있음을 보여줍니다.

IIFE를 가장 잘 사용하는 것은 초기화 설정 기능을 만들고 글로벌 스코프의 다른 변수와 **이름 충돌(naming collisions)**을 피하거나 글로벌 네임 스페이스를 오염시키지 않는 것입니다. 예를 들어 봅시다.

<script src="https://cdnurl.com/somelibrary.js"></script>

우리 코드에서 사용할 수 있는 글로벌 함수를 가진 라이브러리 somelibrary.js에 대한 링크가 있다고 가정합니다. 그러나 이 라이브러리에는 우리가 사용하지 않는 createGraphdrawGraph 두 가지 메서드가 있습니다. 왜냐하면 이 메서드에는 버그가 있기 때문입니다. 그리고 우리는 스스로 createGraphdrawGraph 메서드를 구현하려고 합니다.

  • 해결하는 한 가지 방법은 스크립트의 구조를 변경하는 것입니다.
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function createGraph() {
      // createGraph logic here
   }
   function drawGraph() {
      // drawGraph logic here
   }
</script>

이 솔루션을 사용할 때 라이브러리가 제공하는 두 가지 메서드를 재정의합니다.

  • 이 문제를 해결하는 또 다른 방법은 우리가 함수의 이름을 변경하는 것입니다.
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function myCreateGraph() {
      // createGraph logic here
   }
   function myDrawGraph() {
      // drawGraph logic here
   }
</script>

이 솔루션을 사용할 때 또한 해당 함수 호출을 새 함수 이름으로 변경합니다.

  • 다른 방법은 IIFE를 사용하는 것입니다.
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
  const graphUtility = (function() {
    function createGraph() {
      // createGraph logic here
    }
    function drawGraph() {
      // drawGraph logic here
    }
    return {
      createGraph,
      drawGraph
    }
  })
</script>

이 솔루션에서는 IIFE의 결과인 createGraphdrawGraph의 두 가지 메서드를 포함하는 객체를 리턴하는 유틸리티 변수를 만들고 있습니다.

IIFE가 해결하는 또 다른 문제는 아래의 예에 있습니다

var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
  li[i].addEventListener('click', function (e) {
    console.log(i);
  })
}

list-group 클래스를 가진 ul 엘리먼트가 있고 5 개의 li 자식 엘리먼트를 가지고 있다고 가정해봅시다. 그리고 각각의 li 엘리먼트를 클릭 할 때 console.logi 값을 원합니다.

그러나 이 코드에서 우리가 원하는 동작은 작동하지 않습니다. 대신 아무 li 엘리먼트를 클릭하면 5가 로그로 기록됩니다. 우리가 겪고있는 문제는 클로저의 작동 방식 때문입니다. 클로저는 단순하게 현재 스코프, 부모 함수 스코프 및 글로벌 스코프에서 변수의 참조를 기억하는 함수의 기능입니다. 글로벌 범위에서 var 키워드를 사용하여 변수를 선언할 때 분명히 우리는 글로벌 변수 i를 만듭니다. 따라서 li 엘리먼트를 클릭하면 5가 기록되는데 그 이유는 나중에 콜백 함수에서 i를 참조할 때의 i 값이기 때문입니다.

  • 이에 대한 한 가지 해결책은 IIFE입니다.
var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
  (function (currentIndex) {
    li[currentIndex].addEventListener('click', function (e) {
      console.log(currentIndex);
    })
  })(i);
}

이 솔루션은 IIFE가 모든 반복에 대해 새로운 스코프를 작성하고 우리가 i의 값을 캡처하여 currentIndex 매개 변수로 전달하기 때문에 작동합니다. 그래서 우리가 IIFE를 호출할 때 currentIndex 값이 매 반복 때마다 다릅니다.

27. Function.prototype.apply 사용법은 무엇인가?

apply는 호출 시점에 해당 함수의 this 또는 “소유자” 객체를 지정하는 함수를 호출합니다.

const details = {
  message: 'Hello World!'
};

function getMessage() {
  return this.message;
}

getMessage.apply(details); // returns 'Hello World!

이 메서드는 Function.prototype.call과 같이 동작하지만 유일한 차이점은 인수를 전달하는 방법입니다. apply에서는 인수를 배열로 전달합니다.

const person = {
  name: "Marko Polo"
};

function greeting(greetingMessage) {
  return `${greetingMessage} ${this.name}`;
}

greeting.apply(person, ['Hello']); // returns "Hello Marko Polo!"

28. Function.prototype.call 사용법은 무엇인가?

call은 호출 시점에 this 또는 “소유자” 객체를 지정하는 함수를 호출합니다.

const details = {
  message: 'Hello World!'
};

function getMessage(){
  return this.message;
}

getMessage.call(details); // returns 'Hello World!'

이 메서드는 Function.prototype.apply과 같이 동작하지만 유일한 차이점은 인수를 전달하는 방법입니다. call에서는 모든 인수에 대해 쉼표 , 로 구분하여 직접 전달합니다.

const person = {
  name: "Marko Polo"
};

function greeting(greetingMessage) {
  return `${greetingMessage} ${this.name}`;
}

greeting.call(person, 'Hello'); // returns "Hello Marko Polo!"

29. Function.prototype.applyFunction.prototype.call의 차이점은 무엇인가?

applycall의 유일한 차이점은 호출되는 함수에서 인수를 전달하는 방법입니다. apply에서는 인수를 배열로 전달하고 call에서는 인수를 하나씩 직접 전달합니다.

const obj1 = {
  result:0
};

const obj2 = {
  result:0
};

function reduceAdd(){
  let result = 0;
  for(let i = 0, len = arguments.length; i < len; i++){
    result += arguments[i];
  }
  this.result = result;
}

reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // returns 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // returns 15

30. Function.prototype.bind의 사용법은 무엇인가?

bind 메서드는 특정한 this 값이나 “소유자” 객체로 묶인 새로운 함수를 리턴하므로 나중에 코드에서 사용할 수 있습니다. call, apply 메서드는 bind 메서드처럼 새로운 함수를 리턴하는 대신 즉시 함수를 호출합니다.

import React from 'react';

class MyComponent extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      value : "",
    }
    this.handleChange = this.handleChange.bind(this);
    // "handleChange"메서드를 "MyComponent"컴포넌트에 바인드
  }

  handleChange(e){
    // do something amazing here
  }

  render(){
    return (
      <>
        <input
          type={this.props.type}
          value={this.state.value}
          onChange={this.handleChange}
        />
      </>
    )
  }
}

31. **함수형 프로그래밍(Functional Programming)**이란 무엇이며, **함수형 언어(functional language)**로서 후보가 되는 JavaScript의 특징은 무엇인가?

함수형 프로그래밍은 전달되는 인수가 변이나 변경없이 어떤 값을 계산하는 표현식을 사용하여 함수로 어플리케이션을 만드는 방법에 대한 선언적 프로그래밍 패러다임 또는 패턴입니다. (How)

JavaScript의 Array에는 함수형 프로그래밍 세계에서 가장 유명한 함수인 map, filter, reduce 메서드가 있습니다. 그것들의 유용성 때문에 그리고 이러한 함수를 순수하게 만드는 배열을 변이하거나 변경하지 않기 때문에 JavaScript는 함수형 프로그래밍 언어의 특징인 클로저 및 **고차 함수(Higher Order Functions)**를 지원합니다.

  • map 메서드는 배열의 모든 요소에서 제공된 콜백 함수를 호출한 결과로 새로운 배열을 만듭니다.
const words = ["Functional", "Procedural", "Object-Oriented"];

const wordsLength = words.map(word => word.length);
  • filter 메서드는 콜백 함수에서 테스트를 통과하는 모든 요소로 새로운 배열을 만듭니다.
const data = [
  { name: 'Mark', isRegistered: true },
  { name: 'Mary', isRegistered: false },
  { name: 'Mae', isRegistered: true }
];

const registeredUsers = data.filter(user => user.isRegistered);

reduce 메서드는 누산기(accumulator)에 배열의 각 요소(왼쪽에서 오른쪽으로)를 감소시켜 하나의 값으로 함수를 적용합니다.

const strs = ["I", " ", "am", " ", "Iron", " ", "Man"];
const result = strs.reduce((acc, currentStr) => acc + str, "");

32. **고차 함수(Higher Order Functions)**란 무엇인가?

고차 함수는 함수를 리턴하거나 함수 값을 가진 인수를 받을 수 있는 함수입니다.

function higherOrderFunction(param,callback) {
  return callback(param);
}

33. 함수는 왜 **일급 객체(First-class Objects)**라 하는가?

JavaScript의 함수는 다른 값으로 취급되므로 일급 객체입니다. 함수는 변수에 할당 할 수 있고, 메서드라고 불리는 객체의 속성이 될 수 있고, 배열의 아이템도 될 수 있으며, 함수에 인수로 전달 될 수 있습니다. 그리고 함수의 값으로 리턴 될 수도 있습니다.

34. 직접 Array.prototype.map 메서드를 구현하시오.

function map(arr, mapCallback) {
  // 먼저 전달된 매개 변수가 올바른지 확인합니다.
  if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
    return [];
  } else {
    let result = [];
    // 이 함수를 호출 할 때마다 results 배열을 만듭니다.
    // 원래 배열을 변형하고 싶지 않기 때문입니다.
    for (let i = 0, len = arr.length; i < len; i++) {
      result.push(mapCallback(arr[i], i, arr));
      // 'result'배열에서 mapCallback의 결과를 푸시
    }
    return result; // result 배열 리턴
  }
}

Array.prototype.map 메서드는 MDN 설명에 의하면,

map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 리턴합니다.

35. 직접 Array.prototype.filter 메서드를 구현하시오.

function filter(arr, filterCallback) {
  // 먼저 전달된 매개 변수가 올바른지 확인합니다.
  if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function')
  {
    return [];
  } else {
    let result = [];
    // 이 함수를 호출 할 때마다 results 배열을 만듭니다.
    // 원래 배열을 변형하고 싶지 않기 때문입니다.
    for (let i = 0, len = arr.length; i < len; i++) {
      // filterCallback의 리턴 값이 true나 "truthy"인지 확인
      if (filterCallback(arr[i], i, arr)) {
      // 조건이 true이면 'result'배열에서 현재 아이템을 푸시합니다.
        result.push(arr[i]);
      }
    }
    return result; // result 배열 리턴
  }
}

Array.prototype.filter 메서드는 MDN 설명에 의하면,

filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 리턴합니다.

36. 직접 Array.prototype.reduce 메서드를 구현하시오.

function reduce(arr, reduceCallback, initialValue) {
  // 먼저 전달된 매개 변수가 올바른지 확인합니다.
  if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function')
  {
    return [];
  } else {
    // initialValue가 함수에 전달되지 않으면
    let hasInitialValue = initialValue !== undefined;
    let value = hasInitialValue ? initialValue : arr[0];
    // 첫 번째 배열 항목을 initialValue로 사용합니다.

    // 그런 다음 initialValue가 함수에 전달되지 않으면 인덱스 1에서 루프를 시작합니다. 그렇지않고 initialValue가 있으면 0에서 시작합니다.
    for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
      // 그런 다음 모든 반복에 대해 reduceCallback의 결과를 변수 값에 할당합니다.
      value = reduceCallback(value, arr[i], i, arr);
    }
    return value;
  }
}

Array.prototype.reduce 메서드는 MDN 설명에 의하면,

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 리턴합니다.