모던 자바스크립트 Deep Dive - 27. 배열

둡둡·2024년 2월 12일

Modern Javascript Deep Dive

목록 보기
28/49

27.1. 배열이란?

  • 여러 개의 값을 순차적으로 나열한 자료 구조
  • 요소 (element): 배열이 가지고 있는 값
    • 자바스크립트의 모든 값을 배열의 요소가 될 수 있음
  • index (인덱스): 요소는 자신의 위치를 나타내는 0 이상의 정수를 가짐
  • length 프로퍼티: 배열의 길이, 요소의 개수
    • for문 배열 순회 가능
  • 자바스크립트는 배열 타입이 존재하지 않음 -> 객체 타입
    • 일반 객체와의 차이: 값의 순서, length 프로퍼티
    • 처음 또는 마지막, 특정 위치부터 순차적으로 요소에 접근 가능한 장점

27.2. 자바스크립트 배열은 배열이 아니다

  • 일반적인 의미의 배열은 각 요소가 동일한 데이터 크기를 가지며, 빈틈없이 연속적으로 나열된 자료구조
    • 인덱스를 통해 효율적이고 고속으로 동작
  • 자바스크립트의 배열은 동일한 크기도 아니며 연속적이지 않은 희소 배열
    • 일반적인 배열의 동작을 흉내낸 특수한 객체
    • 배열의 요소는 실제로 프로퍼티의 값
  • 장단점
      1. 일반적인 배열은 인덱스를 통해 빠르게 접근 가능하지만, 삽입 또는 삭제하는 경우 비효율적
      1. 자바스크립트 배열은 해시 테이블로 구현된 객체이므로 성능적으로는 느리지만, 삽입 또는 삭제하는 경우 상대적으로 빠른 성능

27.3. length 프로퍼티와 희소 배열

  • length 프로퍼티: 요소의 개수 = 배열의 길이
    • 빈 배열인 경우 0, 아닌 경우 가장 큰 인덱스 + 1
    • 요소 추가 및 삭제 시 자동 갱신
  • 명시적으로 할당 가능
    • 실제 length 값보다 작은 값을 할당하면 배열의 길이가 줄어듦
    • 큰 값은 할당해도 늘어나지 않음
      • 아무 변화 X, 메모리 공간을 확보하거나 빈 요소를 생성하지 않음
  • 희소 배열: 요소가 연속적으로 위치하지 않고 일부가 빈 배열
    • 희소 배열의 length 값과 실제 요소의 개수는 일치하지 않음
    • 실제 요소 개수보다 length가 항상 큼
    • 성능 및 메모리 등을 위해 사용하지 않는 것을 권장

27.4. 배열 생성

27.4.1. 배열 리터럴

  • 요소를 쉼표로 구분하여 대괄호([])로 묶어 표시
  • 프로퍼티 키 없이 값만 존재
  • 요소를 추가하지 않으면 length 0인 빈 배열 생성
  • 요소를 생략하면 희소 배열 생성

27.4.2. Array 생성자 함수

  • Array 생성자 함수: 전달된 인수의 개수에 따라 다른 배열 생성
    • 1개의 숫자인 인수를 전달하는 경우: length = 인수인 희소 배열 생성
    const arr = new Array(10);
    console.log(arr); // [empty * 10]
    console.log(arr.length); // 10
    • 전달된 인수가 없는 경우 빈 배열 생성 = 배열 리터럴([])
    • 2개 이상의 인수나 숫자가 아닌 경우: 인수를 요소로 갖는 배열 생성
    new Array(1, 2, 3); // [1, 2, 3]
    new Array({}); // [{}]
  • new 생략해도 생성자 함수 동작 -> new.target 확인

27.4.3. Array.of

  • ES6 도입 Array.of: 전달된 인수를 요소로 갖는 배열 생성
  • 전달된 인수가 1개이거나 숫자가 아니어도 생성

26.4.4. Array.from

  • ES6 도입 Array.from: 유사 배열 객체 또는 이터러블 객체를 배열로 변환
  • 두 번째 인수로 콜백 함수 전달 가능
Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]

27.5. 배열 요소의 참조

  • 대괄호([]) 안에 인덱스로 배열 요소에 접근
  • 존재하지 않는 요소는 undefined 반환

27.6. 배열 요소의 추가와 갱신

  • 동적으로 추가 및 갱신 가능
    1. 존재하지 않는 인덱스로 값을 할당하면 새로운 요소 추가
    • length 프로퍼티 값 자동 갱신
    1. 존재하는 요소에 값을 재할당하면 요소 값 갱신
  • 인덱스는 반드시 0 이상의 정수로 사용해야 함
    • 정수 이외의 값을 사용하면 프로퍼티가 생성됨
    • length 값에 영향 없음

27.7. 배열 요소의 삭제

  • 자바스크립트 배열은 객체이므로 delete 연산자 사용 가능
    • delete 사용 시 희소 배열이 됨 -> 사용 권장 X
  • Array.prototype.splice (삭제 시작 인덱스, 삭제할 요소 수) 권장

27.8. 배열 메서드

    1. 원본 배열을 변경하는 메서드
    1. 원본 배열을 변경하지 않고 새로운 배열을 반환하는 메서드
    • 주로 ES5부터 도입된 메서드
    • 사용 권장

27.8.1. Array.prototype.isArray

  • 배열 여부 판별
  • 전달된 인수가 배열이면 true, 아니면 false

27.8.2. Array.prototype.indexOf

  • 인수로 전달된 요소를 검색하여 인덱스 반환
    • 중복되는 요소가 있다면 첫 번째 요소의 인덱스 반환
    • 존재하지 않으면 -1 반환
  • 특정 요소의 존재 여부 판별 시 사용
    • ES7 도입된 includes 대체 가능

27.8.3. Array.prototype.push

  • 인수로 전달받은 모든 값을 마지막 요소로 추가 후 변경된 length 반환
  • 원본 배열 변경
  • 성능 면에서 좋지 않으므로 권장 X
      1. 추가할 요소가 하나라면 length 권장
      1. ES6 스프레드 문법 권장
const arr = [1, 2];
arr[arr.length] = 3; // === arr.push(3);
console.log(arr); // [1, 2, 3]

const newArr = [...arr, 3];
console.log(newArr); // [1, 2, 3, 3]

27.8.4. Array.prototype.pop

  • 마지막 요소를 제거하고 제거한 요소를 반환
    • 빈 배열인 경우 undefined 반환
  • 원본 배열 변경
  • push와 함께 스택(stack, LIFO) 구현 가능

27.8.5. Array.prototype.unshift

  • 인수로 전달받은 모든 값을 배열의 선두 요소로 추가 후 변경된 length 값 반환
  • 원본 배열 변경
    • ES6 스프레드 문법 사용 권장

27.8.6. Array.prototype.shift

  • 첫 번째 요소를 제거하고 제거한 요소 반환
    • 빈 배열인 경우 undefined 반환
  • 원본 배열 변경
  • push와 함께 큐(queue, FIFO) 구현 가능

27.8.7. Array.prototype.concat

  • 인수로 전달된 값을 배열의 마지막 요소로 추가한 다음 새로운 배열 반환
    • 배열을 전달받는 경우 원본 배열을 해체하여 새로운 배열의 요소로 추가함
  • 원본 배열은 변경하지 않음
  • push, unshift 대체 가능
    • [앞에 추가할 요소].concat([뒤에 추가할 요소])
    • ES6 스프레드 문법으로도 대체 가능 -> 사용 권장

27.8.8. Array.prototype.splice

  • 배열의 중간에 요소를 추가하거나 제거 가능
    • start: 제거를 시작할 인덱스
    • deleteCount: start부터 제거할 요소의 개수
      • 0 지정하면 제거하지 않고 삽입만 수행
    • items: 제거한 위치에 삽입할 요소들의 목록
      • 생략하면 제거만 수행
  • 원본 배열 변경
  • indexOf와 함께 제거하려는 특정 요소의 인덱스를 얻어 제거 가능
  • filter 메서드로도 가능 -> 중복 요소 모두 제거됨

27.8.9. Array.prototype.slice

  • 전달된 범위의 요소들을 복사하여 배열로 반환
    • start: 복사를 시작할 인덱스, 음수의 경우 뒤에서부터
    • end: 이전 인덱스까지 복사 후 종료(미포함), 생략 시 length 프로퍼티 값
    • 모두 생략 시 원본 배열의 전체 복사본 반환 -> 얕은 복사
  • 원본 배열은 변경하지 않음
  • arguments, HTMLCollections, NodeList 등 유사 배열 객체를 배열로 변환
    • Array.from 대체 가능

27.8.10. Array.prototype.join

  • 모든 요소를 문자열로 변환 후 인수로 전달받은 문자(구분자)로 연결한 문자열 반환
    • 구분자 생략 가능
    • 기본 구분자는 콤마(,)
const arr = [1, 2, 3, 4];
arr.join(); // '1,2,3,4'
arr.join(''); // '1234'
arr.join(':'); // '1:2:3:4'

27.8.11. Array.prototype.reverse

  • 원본 배열의 순서를 반대로 변경하여 반환
  • 원본 배열 변경

27.8.12. Array.prototype.fill

  • ES6 도입: 배열의 모든 값을 인수로 전달받은 값으로 변경
    • 두 번째 인수로 시작할 인덱스 전달 가능
    • 세 번째 인수로 멈출 인덱스 전달 가능
  • 원본 배열 변경
  • 모든 요소를 하나의 값으로만 변경 가능
    • Array.from 메서드로 대체 가능

27.8.13. Array.prototype.includes

  • ES7 도입: 특정 요소가 포함되었는 지 판별하여 boolean 반환
    • 첫 번째 인수로 검색할 요소(대상) 지정
    • 두 번재 인수로 검색을 시작할 인덱스 지정 가능, 생략 시 처음부터(0)
      • 음수 전달하면 length + index
  • indexOf보다 가독성이 좋음, 추가 판별이 필요 없음

27.8.14. Array.prototype.flat

  • ES10 도입: 인수로 전달한 깊이만큼 재귀적으로 배열을 평탄화
    • 인수 생략하면 기본값 1
    • Infinity 전달하면 모든 중첩 배열을 평탄화
// 기본값 1
[1, [2, [3, [4]]]].flat(); // [1, 2, [3, [4]]]
[1, [2, [3, [4]]]].flat(2); // [1, 2, 3, [4]]
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4]

27.9. 배열 고차 함수

  • 고차 함수(HOF): 함수를 인수로 전달받거나 함수를 반환하는 함수
  • 불변성을 지향하는 함수형 프로그래밍 기반
    • 조건문과 반복문을 제거하여 복잡성 해결
    • 변수의 사용을 억제하여 상태 변경을 피함
    • 순수 함수를 통해 부수 효과를 최대한 억제하고 안정성을 높이고자 함

27.9.1. Array.prototype.sort

  • 배열의 요소 정렬하여 반환
  • 원본 배열 변경
  • 기본적으로 오름차순
    • 내림차순으로 정렬하려면 sort + reverse
  • 정렬 순서는 유니코드 코드 포인트의 순서 (문자열로 변환하여 정렬)
    • 숫자를 정렬할 때는 비교 함수를 인수로 전달해야 함
    • 비교 함수의 반환값이 0보다 작으면 첫 번째 우선 정렬, 크면 두 번째 우선 정렬
const point = [40, 100, 1, 5, 2, 25, 10];

// 오름차순 정렬
points.sort((a, b) => a - b);
console.log(points) // [1, 2, 5, 10, 25, 40, 100];

// 내림차순 정렬
points.sort((a, b) => b - a);
console.log(points) // [100, 40, 25, 10, 5, 2, 1];
  • ES10부터 timsort 알고리즘으로 변경됨

27.9.2. Array.prototype.forEach

  • 자신의 내부에서 반복문을 실행하는 메서드
    • for문 대체할 수 있는 고차 함수
  • 원본 배열은 변경하지 않음
    • 콜백 함수를 통해 변경할 수는 있음
  • 호출한 배열의 요소값과 인덱스, 배열 자체(this) 전달 가능
    • 화살표 함수를 사용하면 상위 스코프의 this 그대로 참조 가능
  • 항상 undefined 반환
  • break, continue 사용 불가

27.9.3. Array.prototype.map

  • 자신을 호출한 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출
  • 원본 배열은 변경하지 않음
  • 콜백 함수들의 반환값으로 구성된 새로운 배열 반환
    • 새로운 배열의 length === 호출한 배열의 length (1:1)
  • 반환값 외에 forEach와 유사함

27.9.4. Array.prototype.filter

  • 모든 요소를 순회하면서 콜백 함수의 반환값이 true인 요소로 구성된 새로운 배열 반환
    • 새로운 배열의 length <= 호출한 배열의 length
  • 원본 배열은 변경하지 않음
  • 반환값 외에 forEach, map과 유사함
  • 특정 요소 하나만 제거하려면 splice 사용

27.9.5. Array.prototype.reduce

  • 모든 요소를 순회하며 콜백 함수의 반환값을 다음 콜백 함수의 첫 번째 인수로 전달 후 하나의 결과값으로 반환
    • 두 번째 인수로 초기값 전달 가능 -> 생략 가능하지만 전달하는 것을 권장
  • 원본 배열은 변경하지 않음
  • 자신을 호출한 배열의 모든 요소를 순회하며 하나의 누적된 결과값을 필요한 경우 사용
    • 평균, 최대값, 요소의 중복 횟수, 중첩 배열 평탄화, 중복 요소 제거 등
// 평균 구하기
const values = [1, 2, 3, 4, 5, 6];
const average = values.reduce(
  (acc, cur, i, { length }) => {
    // 마지막 순회라면 누적 합산 나누기 요소의 개수로 평균 반환
    return i === length - 1 
      ? (acc + cur) / length 
      : acc + cur;
}, 0);
console.log(average); // 3.5

27.9.6. Array.prototype.some

  • 모든 요소를 순회하며 콜백 함수의 반환값이 한 번이라도 참이면 true, 아니면 false 반환
    • 콜백 함수를 통해 정의한 조건을 만족하는 요소가 1개 이상 존재하는지 확인
  • 빈 배열인 경우 항상 false 반환

27.9.7. Array.prototype.every

  • 모든 요소를 순회하며 콜백 함수의 반환값이 모두 참이면 true, 아니면 false 반환
    • 모든 요소가 정의한 조건을 모두 만족하는지 확인
  • 빈 배열인 경우 항상 true 반환

27.9.8. Array.prototype.find

  • ES6 도입: 모든 요소를 순회하며 콜백 함수의 반환값이 true인 첫 번째 요소 반환
  • 존재하지 않는다면 undefined 반환
  • find의 결과값은 배열이 아닌 해당 요소값
    • filter는 새로운 배열

27.9.9. Array.prototype.findIndex

  • ES6 도입: 모든 요소를 순회하며 콜백 함수의 반환값이 true인 첫 번째 요소의 인덱스 반환
  • 존재하지 않는다면 -1 반환

27.9.10. Array.prototype.flatMap

  • ES10 도입: map을 통해 생성된 새로운 배열을 평탄화
  • map + flat 순차적으로 실행하는 기능
  • 단, 평탄화 깊이는 지정할 수 없이 1단계만 가능
    • 지정이 필요한 경우 map().flat() 각각 호출

[출처] 모던 자바스크립트, Deep Dive

profile
괴발개발라이프

0개의 댓글