CH27. 배열
27.1 배열이란?
- 배열이 가지고 있는 값: 요소
- 모든 값은 배열의 요소가 될 수 있음
- 인덱스를 가짐
- 대괄호 내에 접근하고 싶은 요소의 인덱스 지정
- 배열의 길이를 나타내는 length 프로퍼티를 가짐
const arr = ['apple', 'banana', 'orange'];
- for 문을 통해 순차적으로 요소에 접근할 수 있음
- JS에 배열이라는 타입은 존재하지 않음 (배열은 객체 타입)
- 배열의 프로토타입 객체는 Array.prototype
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
typeof arr // object
- 일반 객체와 구별되는 배열의 특징
! 배열의 장점- 처음부터 순차적으로 요소에 접근 가능- 마지막부터 역순으로 요소 접근 가능- 특정 위치 접근 가능
27.2 자바스크립트 배열은 배열이 아니다
- 배열의 요소는 하나의 데이터 타입으로 통일되어 있으며, 서로 연속적으로 인접해 있음 (밀집 배열)- 일반적 의미의 배열은 각 요소가 동일한 데이터 크기를 가지며, 빈틈없이 연속적으로 이어져 있으므로 인덱스를 통해 단 한 번의 연산으로 임의의 요소에 접근할 수 있음. (시간 복잡도 O(1)) : 효율적이고 고속으로 동작함
!! JS 배열은 이런 일반적인 의미의 배열과 다름- 배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 가지지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있음 (희소배열)
const arr = [
'string',
10,
true,
NaN,
[],
{},
function() {}
]; // 어떤 타입의 값이라도 배열의 요소가 될 수 있음
27.3 length 프로퍼티와 희소 배열
- 배열의 길이를 나타내는 0 이상의 정수를 값으로 가짐
- 배열에 요소를 추가하거나 삭제하면 자동 갱신
- 현재 프로퍼티 값보다 작은 숫자 값을 할당하면 배열의 길이가 줄어듦
const arr = [1, 2, 3, 4, 5];
arr.length = 3;
console.log(arr); // [1, 2, 3]
- length 프로퍼티 값보다 큰 숫자 값을 할당하는 경우?
: length 프로퍼티 값은 변경되지만, 실제 배열의 길이가 늘어나지는 않음
const arr = [1];
arr.length = 3;
console.log(arr.length);
console.log(arr); // [1, empty * 2]
- 배열의 요소가 연속적으로 위치하지 않고 일부가 비어 있는 배열: 희소 배열
- 중간이나 앞 부분이 비어 있을 수 있음
- 희소배열은 length와 배열 요소의 개수가 일치하지 않음
- 희소배열의 length는 희소 배열의 실제 요소 개수보다 언제나 큼
- 배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 최선
27.4 배열 생성
27.4.1 배열 리터럴
- 배열 리터럴은 0개 이상의 요소를 쉼표로 구분하고 []로 묶는 것
- 객체 리터럴과 달리 프로퍼티 키가 없고 값만 존재함
const arr = [1, 2, 3];
console.log(arr.length); // 3
const arr = [];
console.log(arr.length); // 0
// 배열 리터럴에 요소를 생략하면 희소 배열이 생성됨
const arr = [1, , 3];
console.log(arr.length); // 3
console.log(arr); // [1, empty, 3]
console.log(arr[1]); // undefined
27.4.2 Array 생성자 함수
- 전달된 인수의 개수에 따라 다르게 동작함
const arr = new Array(10);
console.log(arr); // [empty * 10]
console.log(arr.length); // 10
// ===========================
// 배열은 요소를 최대 2^32 - 1 개 가질 수 있음
// 전달된 인수가 음수면 에러 발생
new Array(-1) // RangeError
// 전달된 인수가 없는 경우 빈 배열 생성
new Array(); // []
// 전달된 인수가 1개지만, 숫자가 아니면 인수를 요소로 갖는 배열을 생성함
new Array({}); // [{}]
27.4.3 Array.of
- Array 생성자 함수와 다르게 전달된 인수가 1개이고, 숫자이더라도 인수를 요소로 갖는 배열을 생성함
Array.of(1); // [1]
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(1); // ['string']
27.4.4 Array.from
- 유사 배열 객체 또는 이터러블 객체를 인수로 전달받아 배열로 변환하여 반환함
Array.from({length:2, 0: 'a', 1:'b'}); // ['a', 'b']
Array.from('Hello'); // ['H', 'e', 'l', 'l', 'o']
- 두 번째 인수로 전달한 콜백 함수를 통해 값을 만들면서 요소를 채울 수 있음
Array.from({length:3}); // [undefined, undefined, undefined]
Array.from({length:3}, (_, i) => i); // [0, 1, 2]
27.5 배열 요소의 참조
- 대괄호 안 인덱스로 접근
- 존재하지 않는 요소에 접근하면 undefined가 반환됨
const arr = [1, 2];
console.log(arr[0]); // 1
console.log(arr[2]); // undefined
27.6 배열 요소의 추가와 갱신
- 배열에도 요소를 동적으로 추가가능
- 존재하지 않는 인덱스를 사용해 값을 할당하면 새로운 요소가 추가됨
- length 프로퍼티 값은 자동 갱신됨
- 만약, length 프로퍼티 값보다 큰 인덱스로 새로운 요소를 추가하면 희소 배열이 됨
- 이때, 인덱스로 요소에 접근하여 명시적으로 값을 할당하지 않은 요소는 생성되지 않음
- 이미 요소가 존재하는 요소에 값을 재할당하면 갱신됨
const arr = []
// 배열 요소의 추가
arr[0] = 1;
arr['1'] = 3;
// 프로퍼티 추가
arr['foo'] = 3;
arr.bar = 4;
arr[1.1] = 5;
arr[-1] = 6;
console.log(arr); // [1, 2, foo:3, bar:4, '1.1':5, '-1':6]
// 프로퍼티는 Length에 영향을 주지 않음
console.log(arr.length); // 2
27.7 배열 요소의 삭제
- delete 연산자 사용 -> 객체의 프로퍼티를 삭제함 -> 희소배열 만들어짐 -> 사용하지 않는 것이 좋음
- 희소배열을 만들지 않으면서 배열의 특정 요소를 완전히 삭제하려면 Array.prototype.splice 메서드 사용
const arr = [1, 2, 3];
delete arr[1];
console.log(arr); // [1, empty, 3]
console.log(arr.length); // 3
// ========
const arr = [1, 2, 3];
arr.splice(1, 1); // arr[1] 부터 1개의 요소를 제거
console.log(arr); // [1, 3]
console.log(arr.length); // 2
27.8 배열 메서드
- 원본 배열을 직접 변경하는 메서드와 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환하는 메서드가 있음
const arr = [1];
// push 는 원본 배열을 직접 변경
arr.push(2);
console.log(arr); // [1, 2]
// concat원본 배열을 직접 변경하지 않고 새로운 배열을 생성해 반환함
const res = arr.concat(3);
console.log(arr); // [1, 2]
console.log(res); // [1, 2, 3]
! 가급적 원본 배열을 직접 변경하지 않는 메서드를 사용하는 편이 좋음!
27.8.1 Array.isArray
: 전달된 인수가 배열이면 true, 아니면 false
27.8.2 Array.prototype.indexOf
: 원본 배열에서 인수로 전달된 요소를 검색해 인덱스 반환
- 중복되는 요소가 여러 개 있으면 첫 번째만 반환
- 존재하지 않으면 -1 반환
27.8.3 Array.prototype.push
- 인수로 전달받은 모든 값을 원본 배열의 마지막 요소로 추가함
- 변경된 length 프로퍼티 값을 반환
- 원본 배열을 직접 변경
const arr = [1, 2];
let res = arr.push(3, 4);
console.log(res); // 4
console.log(arr); // [1, 2, 3, 4]
- 성능 면에서 좋지 않으므로, 마지막 추가할 요소가 하나뿐이라면, push 말고 length 프로퍼티 사용하는 것이 좋음
const arr = [1, 2];
arr[arr.length] = 3;
console.log(arr); // [1, 2, 3]
27.8.4 Array.prototype.pop
- 마지막 요소를 제거하고 제거한 요소를 반환
- 원본이 빈 배열이었다면 undefined
- 직접 원본 배열을 변경함
const arr = [1, 2];
let res = arr.pop();
console.log(res); // 2
console.log(arr); // [1]
27.8.5 Array.prototype.unshift
- 인수로 전달받은 모든 값을 원본 배열의 선두에 요소로 추가
- 변경된 length 프로퍼티 값을 반환함
- 원본 배열을 직접 변경함
const arr = [1, 2];
let res = arr.unshift(3, 4);
console.log(res); // 4
console.log(arr); // [3, 4, 1, 2]
27.8.6 Array.prototype.shift
- 원본 배열에서 첫 번째 요소를 제거하고 제거한 요소를 반환함
- 원본이 빈 배열이었다면 undefined
- 직접 원본 배열을 변경함
const arr = [1, 2];
let res = arr.shift();
console.log(res); // 1
console.log(arr); // [2]
27.8.7 Array.prototype.concat
- 인수로 전달된 값들을 원본 배열의 마지막 요소로 추가한 새로운 배열을 반환함
- 인수로 전달한 값이 배열인 경우, 배열을 해체하여 새로운 배열의 요소로 추가함
- 원본 배열은 변경되지 않음
const arr1 = [1, 2];
const arr2 = [3, 4];
let res = arr1.concat(arr2);
console.log(res); // [1, 2, 3, 4]
res = arr1.concat(3);
console.log(res); // [1, 2, 3]
res = arr1.concat(arr2, 5);
console.log(res); // [1, 2, 3, 4, 5]
console.log(arr1); // 원본 배열은 변경 안됨 [1, 2]
- push와 unshift는 concat으로 대체 가능
: 원본 배열을 직접 변경하고 안 하고의 차이
- concat 메서드는 ES6의 스프레드 문법으로 대체 가능
- ES6의 스프레드 문법을 일관성 있게 사용하는 것을 권장
let res = [1, 2].concat([3, 4]);
console.log(res); // [1, 2, 3, 4]
res = [...[1, 2], ...[3, 4]];
console.log(res); // [1, 2, 3, 4]
27.8.8 Array.prototype.splice
- 원본 배열의 중간에 요소를 추가하거나 중간에 있는 요소를 제거하는 경우 사용
- 3개의 매개변수가 있으며, 원본 배열을 직접 변경함
- 제거할 요소의 개수를 0으로 지정하면 아무런 요소도 제거하지 않고 새로운 요소를 삽입
- 세 번째 인수인 추가할 요소들의 목록을 전달하지 않으면 원본 배열에서 지정된 요소를 제거만 함
- 두 번째 인수인 제거할 요소의 개수를 생략하면 첫 번째 인수로 전달된 시작 인덱스부터 모든 요소를 제거함
const arr = [1, 2, 3, 4];
// 원본 배열의 1부터 2개의 요소를 제거하고, 그 자리에 새로운 요소 20, 30을 삽입
const res = arr.splice(1, 2, 20, 30);
// 제거한 요소가 배열로 반환됨
console.log(res); // [2, 3]
// 원본 배열을 직접 변경함
console.log(arr); // [1, 20, 30, 4]
- filter 메서드를 사용하여 특정 요소를 제거할 수도 있지만, 그 특정 요소가 중복된 경우 모두 제거됨
const arr = [1, 2, 3, 1, 2];
function removeAll(array, item) {
return array.filter(v => v !== item);
}
console.log(removeAll(arr, 2)); // [1, 3, 1]
27.8.9 Array.prototype.slice
- 인수도 전달된 범위의 요소들을 복사하여 배열로 반환
- 원본 배열은 변경되지 않음
- 두 개의 매개변수
: start, 복사를 시작할 인덱스. 음수인 경우 배열의 끝 인덱스 의미
: end, 복사를 종료할 인덱스. 생략 가능하며, 기본값은 length 프로퍼티의 값
const arr = [1, 2, 3];
arr.slice(0, 1); // [1]
arr.slice(1, 2); // [2]
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
- 배열의 순서를 반대로 뒤집음
- 원본 배열 변경
- 반환값은 변경된 배열
const arr = [1, 2, 3, 4];
const res = arr.reverse();
console.log(arr); // [3, 2, 1]
console.log(res); // [3, 2, 1]
27.8.12 Array.prototype.fill
- 인수로 전달받은 값을 배열의 처음부터 끝까지 요소로 채움
- 원본 배열 변경
- 두 번째 인수로 요소 채우기를 시작할 인덱스 전달
- 세 번째 인수로 요소 채우기를 멈출 인덱스 전달
const arr = [1, 2, 3];
// 인수로 전달받은 값 0을 배열의 처음부터 끝까지 요소로 채움
arr.fill(0);
console.log(arr); // [0, 0, 0]
// ====
const arr = [1, 2, 3];
// 인수로 전달받은 값 0을 배열의 인덱스 1부터 끝까지 요소로 채움
arr.fill(0, 1);
console.log(arr); // [1, 0, 0]
// ====
const arr = [1, 2, 3, 4, 5];
// 인수로 전달받은 값 0을 배열의 인덱스 1부터 3이전까지 요소로 채움
arr.fill(0, 1, 3);
console.log(arr); // [1, 0, 0, 4, 5]
27.8.13 Array.prototype.includes
- 배열 내에 특정 요소가 포함되어 있는지 확인하여 true/false 반환
- 첫 번째 인수로 검색할 대상을 지정
- 두 번째 인수로 시작할 인덱스 전달
- 만약 두 번째 인수에 음수를 전달하면 length 프로퍼티 값과 음수 인덱스를 합산하여 검색 (length + index)
const arr = [1, 2, 3];
arr.includes(2); // true
arr.includes(102); // false
// 배열에 요소 1이 포함되어있는지 인덱스 1부터 확인
arr.includes(1, 1); // false
// 배열에 요소 3이 포함되어 있는지 인덱스 2(arr.length - 1)부터 확인
arr.includes(3, -1); // true
27.8.14 Array.prototype.flat
- 인수로 전달한 깊이만큼 재귀적으로 배열을 평탄화함
[1, [2, 3, 4, 5]].flat(); // [1, 2, 3, 4, 5]
- 중첩 배열을 평탄화할 깊이를 인수로 전달 가능
- 인수 생략할 경우 기본값은 1
- Infinity를 전달하면 중첩 배열 모두를 평탄화함
27.9 배열 고차 함수
- 함수를 인수로 전달받거나 함수를 반환하는 함수를 고차 함수라고 함
- 고차함수는 외부 상태의 변경이나 가변 데이터를 피하고, 불변성을 지향하는 함수형 프로그래밍에 기반을 둠
? 함수형 프로그래밍
- 순수 함수와 보조 함수의 조합을 통해 로직 내에 존재하는 조건문과 반복문을 제거해
복잡성을 해결하고 변수의 사용을 억제해 상태 변경을 피하려는 프로그래밍 패러다임
27.9.1 Array.prototype.sort
- 배열의 요소를 정렬
- 원본 배열 직접 변경, 정렬된 배열을 반환
- 기본적으로 오름차순으로 요소를 정렬
- 내림차순으로 하고 싶으면 reverse
const alpha = ['b', 'a', 'd', 'f', 'c'];
alpha.sort();
console.log(alpha); // ['a', 'b', 'c', 'd', 'f']
alpha.reverse();
console.log(alpha); // ['e', 'd', 'c', 'b', 'a']
- 숫자를 정렬할 경우에 주의
- 이 경우에는 정렬 순서를 정의하는 비교 함수를 인자로 전달해야 함
- 비교 함수는 양수나 음수 또는 0을 반환해야 함
const points = [40, 100, 1, 5, 2, 25, 10];
// 비교 함수의 반환값이 0보다 작으면 a를 우선하여 정렬
points.sort((a, b) => a - b);
console.log(points); // [1, 2, 5, 10, 25, 40, 100]
27.9.2 Array.prototype.forEach
- for 문을 대체할 수 있는 고차 함수
- 자신의 내부에서 반복문을 실행
- 반복문을 추상화한 고차 함수로서, 반복문을 통해 자신을 호출한 배열을 순회하며 콜백 함수로 전달받아 반복 호출함
- 원본 배열을 변경하지 않음
- 콜백 함수를 통해 원본 배열 변경 가능
- 반환값은 언제나 undefined
const nums = [1, 2, 3];
const pows = [];
// for문
for (let i=0; i< nusm.length; i++) {
pows.push(nums[i] ** 2);
}
console.log(pows); // [1, 4, 9]
// ===================
const nums = [1, 2, 3];
const pows = [];
// forEach 메서드
nums.forEach(item => pows.push(item ** 2));
console.log(pows); // [1, 4, 9]
27.9.3 Array.prototype.map
- 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출함
- 콜백 함수의 반환값들로 구성된 새로운 배열을 반환
- 원본 배열은 변경되지 않음
const nums = [1, 4, 9];
const roots = nums.map(itme => Math.sqrt(item));
console.log(roots); // [1, 2, 3]
console.log(nums); // [1, 4, 9]
27.9.4 Array.prototype.filter
- 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출
- 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환함
- 원본 배열은 변경되지 않음
const nums = [1, 2, 3, 4, 5];
// 모든 요소를 순회하면서 콜백 함수 반복 호출
// 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환
const odds = nums.filter(item => item % 2);
console.log(odds); [1, 3, 5]
27.9.5 Array.prototype.reduce
- 자신을 호출한 배열을 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출
- 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 인수로 전달하면서 콜백 함수를 호출해 하나의 결과값을 만들어 반환
- 원본 배열은 변경되지 않음
- 첫 번째 인수로 콜백 함수, 두 번째 인수로 초기값을 전달받음
const sum = [1, 2, 3, 4].reduce((accumulator, currentValue, index, array) => accumulator + currentValue, 0);
console.log(sum); // 10
- 4개의 인수를 전달받아 배열의 length만큼 총 4회 호출됨
- 이 과정을 반복해 하나의 결과값을 반환함
1. 평균 구하기
2. 최대값 구하기
3. 요소의 중복 횟수 구하기
4. 중첩 배열 평탄화
5. 중복 요소 제거
27.9.6 Array.prototype.some
- 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출함
- 반환 값이 단 한 번이라도 참이면 true, 모두 거짓이면 false를 반환함
- some 메서드를 호출한 배열이 빈 배열인 경우 언제나 false를 반환하므로 주의
[5, 10, 15].some(item => item > 10); // true
[5, 10, 15].some(item => item < 0); // false
[].some(item => item > 3); // false
27.9.7 Array.prototype.every
- 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출함
- 모두 참이면 true, 하나라도 거짓이면 false
- every 메서드를 호출한 배열이 빈 배열인 경우 언제나 true를 반환하므로 주의
[5, 10, 15].every(item => item > 3); // true
[5, 10, 15].every(item => item < 10); // false
[].every(item => item > 3); // true
27.9.8 Array.prototype.find
- 자신을 호출한 배열의 요소를 순회하며 인수로 전달된 콜백 함수를 호출해 반환값이 true인 첫 번째 요소를 반환
- 콜백 함수의 반환값이 true인 요소가 존재하지 않으면 undefined 반환
27.9.9 Array.prototype.findIndex
- 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출해 반환값이 true인 첫 번째 요소의 인덱스를 반환
- true인 요소가 존재하지 않는다면 -1 반환
27.9.10 Array.prototype.flatMap
- map 메서드를 통해 생성된 새로운 배열을 평탄화함
- map과 flat 메서드를 순차적으로 실행하는 효과가 있음
const arr = ['hello', 'world'];
arr.map(x=>x.split('')).flat();
// ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'];
arr.flatMap(x=>x.split(''));
// ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'];
'2022-2 웹개발 스터디' 카테고리의 다른 글
[모던 JS] CH 32. String, CH33. 7번째 데이터 타입 Symbol (1) | 2022.11.08 |
---|---|
[모던 JS] CH 28. Number, CH 29. Math, CH 30. Date, CH 31. RegExp (#16) (0) | 2022.11.05 |
[모던 JS] CH26. ES6 함수의 추가 기능 (#14) (0) | 2022.11.02 |
[모던 JS] CH25. 클래스 (#13) (0) | 2022.11.02 |
[모던 JS] CH24. 클로저 (#12) (0) | 2022.11.01 |