어떤 한 회사에서 라이브 코딩으로 면접을 보게 되었다.
과제는 정말 간단했다. 주어진 시간 동안 React를 이용한 상태 관리를 구현하면 끝이었다.
그러나 면접 질문은 그리 간단하지 않았다.
아니, 어쩌면 정말 간단한 함수들에 대한 질문이었는데 내가 제대로 답하지 못하였다.
결과는 탈락이었지만 그래도 회고겸 공부를 위해 글을 작성하였다.
JS 배열 내장 함수
탐색 및 검색
includes | 특정 요소가 배열에 존재하는지 확인 (true/false 반환) |
indexOf | 요소의 첫 번째 인덱스를 반환 (없으면 -1) |
lastIndexOf | 요소의 마지막 인덱스를 반환 |
find | 조건을 만족하는 첫 번째 요소를 반환 |
findIndex | 조건을 만족하는 첫 번째 요소의 인덱스를 반환 |
some | 조건을 만족하는 요소가 하나라도 있으면 true |
every | 모든 요소가 조건을 만족하면 true |
at | 음수 인데스로도 접근 가능 (새로운 ES 기능) |
const arr = [1, 2, 3, 4, 5, 1]; // result = 0 arr.indexOf(1); // result = 5 arr.lastIndexOf(1); // result = 1 arr.at(-1); arr.at(0) == arr[0]
그렇다면 arr.at(0)과 arr[0] 중 어떤 것이 더 좋을까?
const arr = Array(1_000_000).fill(1); // 테스트: arr.at(0) console.time('at'); for (let i = 0; i < 1_000_000; i++) { const y = arr.at(0); } console.timeEnd('at'); // 테스트: arr[0] console.time('bracket'); for (let i = 0; i < 1_000_000; i++) { const x = arr[0]; } console.timeEnd('bracket');
위 코드처럼 테스트 코드를 만들어 퍼포먼스 시간을 비교해보았으나 둘 다 비슷했다!
하지만 at 함수는 ES2022부터 생긴 기능이므로 오래된 버전에서는 기능을 안할 수도 있다.
대체로 callback 인자들은
(element, index, array) 이다.const scores = [30, 45, 55, 60, 40, 80]; // 맨 앞에 있으니깐 결국 score는 arr의 element 인 것이다. scores.find((score) => (score > 50)); scores.find((element, index, array) => { console.log(`index: ${index}, value: ${element}, array: [${array}]`); return index % 2 === 0 && element >= 50; }; // true scores.some((value) => value > 50) // false scores.every((value) => value > 50)
변형 및 가공
reduce 함수의 callback 인자는
(accumulator, currentValue, currentIndex, array)이다.map | 각 요소를 변형하여 새 배열을 생성 |
filter | 조건을 만족하는 요소만으로 새 배열을 생성 |
reduce | 누적 계산을 통해 하나의 값으로 축약 |
reduceRight | 오른쪽에서 왼쪽으로 누적 계산 |
flat | 다차원 배열을 평탄화 |
flatMap | map + flat(1) |
sort | 배열 정렬 |
reverse | 배열 순서를 뒤집음 |
const arr1 = [1,2,3,4,5]; // arr2 = [2,3,4,5,6] const arr2 = arr1.map((element) => element + 1); // arr3 = [3,4] const arr3 = arr.filter((element, index) => index > 1 && element < 5); // result = 28 const result = arr.reduce((accumulator, element) => accumulator + element, 0);
const arr = [1, [2, [3, [4, 5]]]]; // = arr.flat() arr.flat(1); // [1, 2, [3, [4, 5]]] arr.flat(2); // [1, 2, 3, [4, 5]] arr.flat(3); // [1, 2, 3, 4, 5]
요소 추가 및 제거
push | 마지막에 요소 추가 |
pop | 마지막 요소 제거 |
unshift | 처음에 요소 추가 |
shift | 첫 요소 제거 |
splice | 요소를 추가/제거/교체 |
slice | 배열의 일부분을 잘라 새 배열 생성 (원본 변경 x) |
결합 및 복사
concat | 두 배열을 이어붙여 새 배열 생성 |
join | 배열을 문자열로 변환 |
toString | 배열을 쉼표로 구분된 문자열로 반환 |
copyWithin | 배열 내 특정 위치로 값을 복사 |
fill | 배열의 모든 요소를 특정 값으로 채움. fill 안에 인자가 없으면 undefinded로 할당 |
from | 배열이나 유사 배열(array-like)을 새 배열로 변환 |
const arr = new Array(10).fill(0). const str = "hello"; const chars = Array.from(str); // ['h', 'e', 'l', 'l', 'o']
new를 쓰지 않으면 어떻게 될까?
new를 사용하면 생성자 호출 방식, 안쓰면 함수 호출 방식이지만 동일한 역할을 한다.
const arr = Array.from({ length: 10 }, (_, i) => i); console.log(arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] const arr = Array(10).fill(0).map((_, i) => i);
이 둘은 성능 차이는 거의 없지만,
Array.from은 의도와 구조가 명확해서 협업 시 더 추천되는 경우가 많다고 한다.
반복
forEach | 각 요소에 대해 함수를 실행 |
const arr = [1,2,3,4,5]; arr.forEach((element, index) => {arr[index] = element + 1}); arr.forEach((element, index, array) => {array[index] = element + 1};
위의 두 코드의 차이는 뭘까?
사실 성능의 차이는 없지만, 내가 생각했을 때는 아래 방법이 더 좋은 것 같다.
위의 방법의 경우 원하지 않았던 arr (이름이 같은 변수) 을 가져오거나 실수로 다른 이름을 적을 수도 있다.
정리하며,
지금 나는 이 글을 쓰면서 같은 기능을 하는 코드들의 차이를 분석해보았다.
실제로 이런 질문이 면접에서 나왔었고, 답은 없겠지만 각각의 장단점 또는 이유는 말할 수 있어야 한다.
같은 기능을 만드는데 정말 다양한 방법으로 코딩할 수 있을 것이다.
개발자는 이렇게 서로 다른 방법들을 알고 있어야하며 각각의 차이를 알아야 한다 생각한다.

