자바스크립트 배열은 다양한 데이터 타입의 값을 포함할 수 있는 동적 배열이다.
// 각 요소는 서로 다른 데이터 타입을 가질 수 있다.
const array = [123, 'name', true, function () { }, {}, ['a', 'b']];
// 배열의 이름은 복수형으로 짓는 게 일반적이다.
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.length); // 5
console.log(numbers[0]); // 1
인덱스를 사용해 새로운 요소를 추가한다. 빈 자리는 empty items
로 채워진다.
const numbers = [0, 1, 2];
numbers[numbers.length] = 3; // [0, 1, 2, 3]
numbers[10] = 10; // [0, 1, 2, 3, <6 empty items>, 10]
push()
는 배열의 끝에 새로운 요소를 추가하고, unshift()
는 배열의 앞에 새로운 요소를 추가한다. 두 개 이상의 값을 한 번에 넣을 수도 있다.
const numbers = [0, 1, 2];
numbers.push(3); // [0, 1, 2, 3]
numbers.push(4, 5); // [0, 1, 2, 3, 4, 5]
numbers.unshift(-1); // [-1, 0, 1, 2]
numbers.unshift(-3, -2); // [-3, -2, -1, 0, 1, 2]
concat()
은 두 개 이상의 배열을 결합하거나 배열에 값을 추가할 수 있다.
push()
, unshift()
와 다르게 완전히 새로운 배열을 반환하며, 기존 배열은 변경되지 않는다.
const nums1 = [0, 1];
const nums2 = [2, 3];
const nums3 = nums1.concat(nums2);
// [0, 1, 2, 3]
const nums4 = nums3.concat(nums1, nums2);
// [0, 1, 2, 3, 0, 1, 2, 3]
const nums5 = nums1.concat(10);
// [0, 1, 10]
splice()
는 배열의 내용을 변경하고, 제거된 요소가 담긴 별도의 배열을 새로 반환한다.
Array.splice(start, deleteCount, newItem, newItem, ...)
start
: 변경을 시작할 인덱스
deleteCount
: 제거할 요소의 수
newItem
: 배열에 추가할 요소 (지정하지 않으면 제거만 한다.)
const original = [0, 1, 2];
const spliced = original.splice(0, 2);
console.log(original); // [2]
console.log(spliced); // [0, 1]
const spliced = original.splice(1, 0, 'new');
console.log(original); // [0, 'new', 1, 2]
console.log(spliced); // []
pop()
은 배열의 마지막 요소를 제거하고 반환한다.
const numbers = [0, 1, 2];
const popped = numbers.pop();
console.log(numbers); // [0, 1]
console.log(popped); // 2
slice()
는 배열의 일부를 추출하여 새로운 배열로 반환한다.
const numbers = [0, 1, 2];
const sliced = numbers.slice(1);
console.log(sliced); // [1, 2]
const sliced = numbers.slice(0, 1);
console.log(sliced); // [0]
indexOf()
는 특정 요소를 찾을 수 있는 첫 번째 인덱스를 반환하고,lastIndexOf()
는 마지막 인덱스를 반환한다. 찾을 수 없는 경우엔 -1를 반환한다.
Array.indexOf(searchElement, fromIndex = 0);
Array.lastIndexOf(searchElement, fromIndex = this.length - 1);
searchElement
: 찾는 요소
fromIndex
: 검색을 시작할 인덱스
const numbers = [0, 1, 2, 0, 1, 2];
console.log(numbers.indexOf(1)); // 1
console.log(numbers.lastIndexOf(1)); // 4
console.log(numbers.indexOf(2, 3)); // 5
console.log(numbers.lastIndexOf(2, 3)); // 2
console.log(numbers.indexOf(5)); // -1
console.log(numbers.lastIndexOf(5)); // -1
find()
는 각 요소에 콜백 함수를 실행하여 첫 번째로 true를 반환한 요소를 반환한다. findIndex()
는 요소의 인덱스를 반환한다. 찾을 수 없는 경우엔 -1를 반환한다.
indexOf
보다 복잡한 조건을 사용하여 요소를 찾아야 할 때 사용한다. 객체 배열에서 특정 속성을 가진 요소를 찾는 데 유용하게 쓸 수 있다.
const users = [
{ id: 1, name: 'kim' },
{ id: 2, name: 'lee' },
{ id: 3, name: 'park' }
];
const user = users.find(user => user.id === 2);
console.log(user); // { id: 2, name: 'lee' }
function isPark(element) {
if (element.name === 'park') {
return true;
}
}
const park = users.find(isPark);
console.log(park); // { id: 3, name: 'park' }
const userIndex = users.findIndex(user => user.id === 2);
console.log(userIndex); // 1
includes()
는 배열에 특정 값이 포함되어 있는지 확인한다. 포함되어 있으면 true
를, 포함되어 있지 않으면 false
를 반환한다.
Array.includes(searchElement, fromIndex);
searchElement
: 찾는 요소
fromIndex
: 검색을 시작할 인덱스
const numbers = [0, 1, 2];
console.log(numbers.includes(1)); // true
console.log(numbers.includes(5)); // false
const numbers = [0, NaN, 2];
console.log(numbers.includes(NaN)); // true
forEach()
는 배열의 요소를 순회하며 제공된 함수를 실행한다. 별도의 반환값은 없다.
const numbers = [1, 2, 3];
numbers.forEach(num => console.log(num)); // 1, 2, 3
const double = [];
numbers.forEach(num => double.push(num * 2));
console.log(double); // [2, 4, 6]
map()
은 배열의 각 요소를 순회하며 제공된 함수를 실행하고, 그 결과로 새로운 배열을 생성한다.
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6]
const string = numbers.map(num => num.toString());
console.log(string); // ['1', '2', '3']
const info = numbers.map((num, index) => ({
value: num,
index: index
}));
console.log(info);
// [
// { value: 1, index: 0 },
// { value: 2, index: 1 },
// { value: 3, index: 2 },
// { value: 4, index: 3 },
// { value: 5, index: 4 }
// ]
reduce()
는 배열의 각 요소를 순회하면서 누적 값을 계산한다. 새로운 배열을 생성하는 map()
과 다르게 단일 값을 생성한다.
Arr.reduce(callback, initialValue);
callback
: 각 요소에 처리할 함수이다. 네 가지 인수를 받을 수 있다.
accumulator
: 이전 호출의 반환값currentValue
: 현재 처리 중인 요소currentIndex
: 현재 처리 중인 요소의 인덱스array
: 원본 배열initialValue
: 초기 acc값 (생략하면 첫 번째 요소가 초기값이 된다.)
// 합계 구하기
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 15
// 요소 개수 세기
const fruits = ['apple', 'banana', 'cherry', 'banana'];
const fruitCount = fruits.reduce((count, fruit) => {
count[fruit] = (count[fruit] || 0) + 1;
return count;
}, {});
console.log(fruitCount); // { apple: 1, banana: 2, cherry: 1 }
// 중첩 해제하기
const nestedArray = [[1, 2], [3, 4], [5, 6]];
const flatArray = nestedArray.reduce((acc, cur) => acc.concat(cur), []);
console.log(flatArray); // [1, 2, 3, 4, 5, 6]
some()
은 배열의 요소 중 하나라도 테스트 함수를 통과하면 true를 반환한다. every()
는 배열의 모든 요소가 테스트 함수를 통과했을 때 true를 반환한다.
const numbers = [1, 2, 3];
const negativeNum = numbers.some(num => num < 0); // false
const evenNum = numbers.some(num => num % 2 === 0); // true
const positiveAll = numbers.every(num => num > 0); // true
const evenAll = numbers.every(num => num % 2 === 0); // false
배열이 비어있을 때, some
은 false를 반환하고 every
는 true를 반환한다.
filter()
은 배열의 각 요소를 순회하며 제공된 함수를 실행하고, 그 결과가 true인 요소만 모은 새로운 배열을 반환한다.
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const even = numbers.filter(num => num % 2 === 0);
console.log(even); // [2, 4, 6, 8, 10]
const largerThanFive = numbers.filter(num => num > 5);
console.log(largerThanFive); // [6, 7, 8, 9, 10]
reverse()
는 배열의 순서를 반대로 변경한다.
const numbers = [0, 1, 2];
const reversed = numbers.reverse();
console.log(numbers); // [2, 1, 0]
console.log(reversed); // [2, 1, 0]
sort()
는 배열의 요소를 정렬한다. 기준을 사용자가 정의할 수도 있다.
const numbers = [5, 2, 9, 1, 7];
numbers.sort(); // [1, 2, 5, 7, 9]
// 오름차순, 내림차순
numbers.sort((a, b) => a - b); // [1, 2, 5, 7, 9]
numbers.sort((a, b) => b - a); // [9, 7, 5, 2, 1]
// 문자열 배열 정렬
const strings = ['banana', 'apple', 'cherry', 'date'];
strings.sort(); // ['apple', 'banana', 'cherry', 'date']
// 객체 배열 정렬
const people = [
{ name: 'Kim', age: 21 },
{ name: 'Lee', age: 54 },
{ name: 'Park', age: 36 }
];
people.sort((a, b) => a.age - b.age);
// [
// { name: 'Charlie', age: 20 },
// { name: 'Alice', age: 25 },
// { name: 'Bob', age: 30 }
// ]
toString()
은 배열의 모든 요소를 하나의 문자열로 변환하여 반환한다. 각 요소는 쉼표로 구분되며, 원본 배열은 변경되지 않는다.
const numbers = [0, 1, 2];
const string = numbers.toString(); // "0,1,2"
join()
은 각 요소 사이에 지정된 구분자를 삽입하여 문자열로 변환할 수 있다. 지정하지 않으면 기본적으로 쉼표로 구분된다.
const numbers = [0, 1, 2];
const result1 = numbers.join(); // "0,1,2"
const result2 = numbers.join('-'); // "0-1-2"
새로 생성된 배열은 얕은 복사라는 점을 주의하자.
const original = [{ name: 'kim' }, [1, 2]];
const copied = original.concat();
copied.push(3);
copied[0].name = 'lee';
original[1].push(3);
console.log(original); // [{ name: 'lee' }, [1, 2, 3]]
console.log(copied); // [{ name: 'lee' }, [1, 2, 3], 3]
얕은 복사가 드러나는 대표적인 데이터 타입은 다음과 같다.
{}
)[]
)Function() {...}
)Date
, RegExp
, Error
등의 내장 객체반면에 다음과 같은 타입은 참조 타입이 아닌 값 타입이기 때문에 깊은 복사가 된다.
number
, string
, boolean
, undefined
, null
따라서 배열이나 객체에 값 타입 데이터만 포함되어 있다면 복사 시 얕은 복사가 발생하지 않는다. 하지만 참조 타입이 포함되어 있다면 반드시 얕은 복사가 일어난다.
배열을 깊은 복사 하기 위해선 JSON 변환을 이용하면 된다. JSON.stringify()
를 사용해 배열을 문자열로 변환한 후, JSON.parse()
로 다시 배열로 변환하는 방식이다.
let original = [1, { a: 2 }, [3, 4]];
let copied = JSON.parse(JSON.stringify(original));