배열 생성하기
1. 배열 리터럴
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
2. 배열 생성자
const arr1 = new Array(1, 2, 3);
console.log(arr1); // [1, 2, 3]
const arr2 = new Array(10); // 인수를 하나만 넘겨줄 경우 그 인수가 배열의 길이
arr2.length; // 20
Array 함수
🔑로 표시한 함수는 원본 배열을 변경시킨다.
🔒로 표시한 함수는 원본 배열을 변경시키지 않는다.
Array.isArray(arg)
주어진 인수가 배열이면 true, 배열이 아니면 false를 반환한다.
Array.from()
ES6에서 새롭게 도입된 함수로 특정 객체를 변환하여 새로운 배열을 생성한다. 변환할 수 있는 객체는 아래와 같다.
- 유사 배열 객체(array-like objects): length 프로퍼티와 index된 요소를 가지고 있는 객체
- 이터러블 객체(iterable objects): Map과 Set 객체 및 문자열과 같이 해당 요소를 개별적으로 선택할 수 있는 객체
// 문자열은 iterable이다.
const arr1 = Array.from('Hello');
console.log(arr1); // [ 'H', 'e', 'l', 'l', 'o' ]
// 유사 배열 객체를 새로운 배열을 변환하여 반환한다.
const arr2 = Array.from({ length: 2, 0: 'a', 1: 'b' });
console.log(arr2); // [ 'a', 'b' ]
// Array.from의 두번째 매개변수에게 배열의 모든 요소에 대해 호출할 함수를 전달할 수 있다.
// 이 함수는 첫번째 매개변수에게 전달된 인수로 생성된 배열의 모든 요소를 인수로 전달받아 호출된다.
const arr3 = Array.from({ length: 5 }, function (v, i) { return i; });
console.log(arr3); // [ 0, 1, 2, 3, 4 ]
Array.of()
ES6에서 새롭게 도입된 함수로 전달된 인수를 요소로 갖는 배열을 생성한다. Array.of()는 배열 생성자와는 달리 전달된 인수가 하나고 숫자더라도 인수를 요소로 갖는 배열을 생성한다.
// 전달된 인수가 1개이고 숫자이더라도 인수를 요소로 갖는 배열을 생성한다.
const arr1 = Array.of(1);
console.log(arr1); // // [1]
const arr2 = Array.of(1, 2, 3);
console.log(arr2); // [1, 2, 3]
const arr3 = Array.of('string');
console.log(arr3); // 'string'
Array.fill(value, start, end) 🔑
배열의 시작 인덱스부터 마지막 인덱스 전(마지막 인덱스 자신은 미포함)까지 하나의 값으로 채워주는 함수다. 원본 배열을 직접 변경하며 반환 값은 변형된 배열이다.
value: 배열을 채울 값
start: 시작 인덱스. 기본값 0. option
end: 마지막 인덱스. 기본값 0. option
/* 길이가 6이고 배열의 원소가 모두 0인 배열을 만들기 */
// 리터럴 방법
const zeroArray = [0, 0, 0, 0, 0, 0];
// fill() 함수를 이용한 방법
const zeroArray2 = Array(6).fill(0);
console.log(nullArray); // [0, 0, 0, 0, 0, 0]
[1, 2, 3].fill(4) // [4, 4, 4]
[1, 2, 3].fill(4, 1) // [1, 4, 4] 1번째 인덱스 부터 쭉 4로 채움
[1, 2, 3].fill(4, 1, 2) // [1, 4, 3] 1번째에서 2번째 인덱스 전까지 4로 채움
/* fill()을 사용하여 모두 1의 행렬(2차원 배열) 만들기 */
const arr = new Array(3);
for (let i = 0; i < arr.length; i++) {
arr[i] = new Array(4).fill(1); // 크기가 4이고 1로 채워진 배열 생성
}
console.log(arr[0][0]); // 1
console.log(arr[1][0]); // 1
console.log(arr[2][0]); // 1
Array.prototype.indexOf(searchElement, fromIndex) 🔒
원본 배열에서 인수로 전달된 요소를 검색하여 index를 반환한다. 중복되는 요소가 있을 경우 첫 번째 index를 반환하며 해당하는 요소가 없을 경우 -1을 반환한다. indexOf()는 배열에 요소가 존재하는지 여부를 확인할 때 유용하게 사용된다.
searchElement: 검색할 요소
fromIndex: 검색을 시작할 인덱스. 해당 인수를 생략하면 처음부터 검색한다.
const foods = ['apple', 'banana', 'orange'];
// foods 배열에 'orange'라는 요소가 존재하는지 확인
if (foods.indexOf('orange') === -1) { // == if (!foods.includes('orange')) { }
foods.push('orange');
}
console.log(foods); // ["apple", "banana", "orange"]
Array.prototype.concat(item1, item2, item3, ...) 🔒
인수로 전달된 값들(배열 또는 값)을 원본 배열의 마지막 요소로 추가한 새로운 배열을 반환한다. 인수로 전달한 값이 배열일 경우 배열을 해체하여 새로운 배열의 요소로 추가한다. 이때, 원본 배열은 변경되지 않는다.
const arr1 = [1, 2];
const arr2 = [3, 4];
// 배열 arr2를 원본 배열 arr1의 마지막 요소로 추가한 새로운 배열을 반환
// 인수로 전달한 값이 배열인 경우, 배열을 해체하여 새로운 배열의 요소로 추가한다.
let result = arr1.concat(arr2);
console.log(result); // [1, 2, 3, 4]
// 숫자를 원본 배열 arr1의 마지막 요소로 추가한 새로운 배열을 반환
result = arr1.concat(3);
console.log(result); // [1, 2, 3]
// 배열 arr2와 숫자를 원본 배열 arr1의 마지막 요소로 추가한 새로운 배열을 반환
result = arr1.concat(arr2, 5);
console.log(result); // [1, 2, 3, 4, 5]
// 원본 배열은 변경되지 않는다.
console.log(arr1); // [1, 2]
Array.prototype.join(separator) 🔒
원본 배열의 모든 요소를 문자열로 변환한 후 인수로 전달 받은 값, 즉 구분자(separator)로 연결한 문자열을 반환한다. 구분자는 생략 가능하며 구분자는 , 이다.
const arr = [1, 2, 3, 4];
// 기본 구분자는 ','이다.
// 원본 배열 arr의 모든 요소를 문자열로 변환한 후, 기본 구분자 ','로 연결한 문자열을 반환
let result = arr.join();
console.log(result); // '1,2,3,4';
// 원본 배열 arr의 모든 요소를 문자열로 변환한 후, 빈문자열로 연결한 문자열을 반환
result = arr.join('');
console.log(result); // '1234'
// 원본 배열 arr의 모든 요소를 문자열로 변환한 후, 구분자 ':'로 연결한 문자열을 반환
result = arr.join(':');
console.log(result); // '1:2:3:4'
Array.prototype.push(...items) 🔑
인수로 전달 받은 모든 값을 원본 배열의 마지막 요소로 추가하고 변경된 length 값을 반환한다.
push()와 concat()은 유사하게 동작하지만 미묘한 차이가 있다. push()는 원본 배열을 직접 변경하지만 concat()은 원본 배열을 변경하지 않고 새로운 배열을 반환한다.
const arr1 = [1, 2];
// push 메소드는 원본 배열을 직접 변경한다.
arr1.push(3, 4);
console.log(arr1); // [1, 2, 3, 4]
const arr2 = [1, 2];
// concat 메소드는 원본 배열을 변경하지 않고 새로운 배열을 반환한다.
const result = arr2.concat(3, 4);
console.log(arr2); // [1, 2]
console.log(result); // [1, 2, 3, 4]
또한 인수로 전달 받은 값이 배열인 경우 push()는 배열을 그대로 원본 배열의 마지막 요소로 추가하지만, concat()은 배열을 해체하여 새로운 배열의 마지막 요소로 추가한다.
const arr1 = [1, 2];
// 인수로 전달받은 배열을 그대로 원본 배열의 마지막 요소로 추가한다
arr1.push([3, 4]);
console.log(arr1); // [1, 2, [3, 4]]
const arr2 = [1, 2];
// 인수로 전달받은 배열을 해체하여 새로운 배열의 마지막 요소로 추가한다
const result = arr2.concat([3, 4]);
console.log(arr2); // [1, 2]
console.log(result); // [1, 2, 3, 4]
push()는 성능 면에서 좋지 않다. push()의 경우 배열의 마지막에 요소를 추가하므로 length 프로퍼티를 사용하여 직접 요소를 추가할 수도 있는데 이 방법이 push()를 사용하는 것보다 빠르다.
const arr = [1, 2];
// arr.push(3)와 동일한 처리를 한다. 이 방법이 push()보다 빠르다.
arr[arr.length] = 3;
console.log(arr); // [1, 2, 3]
push()는 원본 배열을 직접 변경하는 부수 효과가 있다. 따라서 push()보다는 ES6의 spread 문법을 사용하는 편이 좋다.
const arr = [1, 2];
// spread 문법
const newArr = [...arr, 3];
// arr.push(3);
console.log(arr); // [1, 2]
console.log(newArr); // [1, 2, 3]
Array.prototype.pop() 🔑
원본 배열에서 마지막 요소를 제거하고 제거한 요소를 반환한다. 원본 배열이 빈 배열이면 undefined를 반환한다. pop()은 원본 배열을 직접 변경한다.
const a = ['a', 'b', 'c'];
const c = a.pop();
// 원본 배열이 변경된다.
console.log(a); // a --> ['a', 'b']
console.log(c); // c --> 'c'
Array.prototype.shift() 🔑
원본 배열에서 첫 요소를 제거하고 제거한 요소를 반환한다. 만약 빈 배열일 경우 undefined를 반환한다. shift()는 원본 배열 자체를 변경한다. shift()는 push()와 함깨 배열을 큐(Queue, FIFO: First In First Out)처럼 동작하게 한다.
const arr = [];
arr.push(1); // [1]
arr.push(2); // [1, 2]
arr.push(3); // [1, 2, 3]
arr.shift(); // [2, 3]
arr.shift(); // [3]
arr.shift(); // []
Array.prototype.unshift(...items) 🔑
unshift()는 shift()와 반대로 원본 배열의 첫 요소로 인수로 전달 받은 값들을 추가한 후 배열의 length를 반환한다. pop()과 동일하게 가변 인자를 받기 때문에 여러 개의 값을 한 번에 추가할 수도 있다.
const arr = [100, 101, 102];
arr.unshift(98, 99);
console.log(arr); // [98, 99, 100, 101, 102]
push()와 unshift()는 성능 측면에서 중요한 차이점이 있다. push()의 시간 복잡도는 0(1)이기 때문에 배열의 크기에 상관없이 상수 시간이 소모된다. 반면 unshift()는 새로운 요소를 위해 기존에 배열에 있던 모든 요소를 한 자리씩 오른쪽으로 이동시켜야 하기 때문에 시간 복잡도가 0(n)이 된다. 따라서 배열에 저장되어 있는 값의 수가 많을수록 그에 비례하여 시간이 오래 걸린다. 성능에 민감한 프로그램을 작성하거나 크기가 아주 큰 배열을 다루는 상황에서는 이러한 상능 차이를 반드시 고려할 필요가 있다.
Array.prototype.reverse()
원본 배열 요소의 순서를 반대로 변경시키며 변경된 배열을 반환한다.
const array = [1, 2, 3, 4, 5];
array.reverse();
console.log(array); // [5, 4, 3, 2, 1]
Array.prototype.slice(start, end) 🔒
인자로 지정된 배열의 부분을 복사하여 반환하는데, 이때 원본 배열은 변경되지 않는다.
start: 복사를 시작할 인덱스. 음수인 경우 배열의 끝에서의 인덱스를 나타낸다. 예를 들어 slice(-2)는 배열의 마지막 2개의 요소를 반환한다.
end: 옵션이며 기본 값은 length.
const items = ['a', 'b', 'c'];
// items[0]부터 items[1] 이전(items[1] 미포함)까지 반환
let res = items.slice(0, 1);
console.log(res); // [ 'a' ]
// items[1]부터 items[2] 이전(items[2] 미포함)까지 반환
res = items.slice(1, 2);
console.log(res); // [ 'b' ]
// items[1]부터 이후의 모든 요소 반환
res = items.slice(1);
console.log(res); // [ 'b', 'c' ]
// 인자가 음수인 경우 배열의 끝에서 요소를 반환
res = items.slice(-1);
console.log(res); // [ 'c' ]
res = items.slice(-2);
console.log(res); // [ 'b', 'c' ]
// 모든 요소를 반환(= 복사본(shallow copy) 생성)
res = items.slice();
console.log(res); // [ 'a', 'b', 'c' ]
slice()에 인자를 전달하지 않으면 원본 배열의 복사본을 생성하여 반환한다. 이때 원본 배열의 각 요소를 얕은 복사(shallow copy)하여 새로운 복사본을 생성한다.
const todos = [
{ id: 1, content: 'HTML', completed: false },
{ id: 2, content: 'CSS', completed: true },
{ id: 3, content: 'Javascript', completed: false }
];
// shallow copy
const _todos = todos.slice(); // const _todos = [...todos];
console.log(_todos === todos); // false
// 배열의 요소는 같다. 즉, 얕은 복사되었다.
console.log(_todos[0] === todos[0]); // true
slice()를 이용해 arguments, HTMLCollection, NodeList와 같은 유사 배열 객체(Array-like Object)를 배열로 변환할 수 있다.
function sum() {
// 유사 배열 객체 => Array
const arr = Array.prototype.slice.call(arguments); // [1, 2, 3]
const arr0 = [].slice.call(arguments); // [1, 2, 3]
// Spread 문법
const arr1 = [...arguments]; // [1, 2, 3]
// Array.from 메소드는 유사 배열 객체를 복사하여 배열을 생성한다.
const arr2 = Array.from(arguments); // [1, 2, 3]
}
console.log(sum(1, 2, 3));
Array.prototype.splice(start, deleteCount, ...items) 🔑
start: 배열에서의 시작 위치 인덱스. start만 지정하면 배열의 start부터 모든 요소를 제거한다.
deleteCount: 시작 위치(start)부터 제거할 요소의 수. deleteCount가 0인 경우 아무런 요소도 제거되지 않는다. 옵션.
items: 삭제한 위치에 추가될 요소들. 만약 아무런 요소도 지정하지 ㅇ낳을 경우 삭제만 한다. 옵션.
splice()는 아래의 세 가지 기능으로 사용될 수 있으며 반환 값은 삭제한 요소들을 가진 배열이다.
- 배열 요소 추가(삭제 안 하고 추가)
- 배열 요소 교체(삭제하고 추가)
- 배열 요소 삭제(추가 안 하고 삭제)
splice()의 가장 일반적인 사용은 배열에서 요소를 삭제하는 것이다.
const items1 = [1, 2, 3, 4];
// items[1]부터 2개의 요소를 제거하고 제거된 요소를 배열로 반환
const res1 = items1.splice(1, 2);
// 원본 배열이 변경된다.
console.log(items1); // [ 1, 4 ]
// 제거한 요소가 배열로 반환된다.
console.log(res1); // [ 2, 3 ]
// -------------------------------------------
const items2 = [1, 2, 3, 4];
// items[1]부터 모든 요소를 제거하고 제거된 요소를 배열로 반환
const res2 = items2.splice(1);
// 원본 배열이 변경된다.
console.log(items2); // [ 1 ]
// 제거한 요소가 배열로 반환된다.
console.log(res2); // [ 2, 3, 4 ]
배열에서 요소를 제거하고 제거한 위치에 다른 요소를 추가한다(교체).
const items = [1, 2, 3, 4];
// items[1]부터 2개의 요소를 제거하고 그자리에 새로운 요소를 추가한다. 제거된 요소가 반환된다.
const res = items.splice(1, 2, 20, 30);
// 원본 배열이 변경된다.
console.log(items); // [ 1, 20, 30, 4 ]
// 제거한 요소가 배열로 반환된다.
console.log(res); // [ 2, 3 ]
또한 배열 중간에 새로운 요소를 추가할 때에도 사용된다(0개를 삭제).
const items = [1, 2, 3, 4];
// items[1]부터 0개의 요소를 제거하고 그자리(items[1])에 새로운 요소를 추가한다. 제거된 요소가 반환된다.
const res = items.splice(1, 0, 100);
// 원본 배열이 변경된다.
console.log(items); // [ 1, 100, 2, 3, 4 ]
// 제거한 요소가 배열로 반환된다.
console.log(res); // [ ]
배열 중간에 배열을 추가할 때에도 사용된다.
const items = [1, 4];
const items2 = [2, 3];
// 원본배열 item를 아규먼트 인자로 items2를 받아서,
// items[1]부터 0개의 요소를 제거하고 그자리(items[1])에 새로운 배열를 추가한다.
// 이렇게 하면 아규먼트가 2차원형태가 되어버린다. ( 1, [ 2, 3 ], 4 )
items.splice(1, 0, items2);
// apply로 items를 주고, [1, 0].concat([2, 3])의 결과 인자 1,0, 2,3을 주게되는 꼴이 된다.
Array.prototype.splice.apply(items, [1, 0].concat([2, 3]));
items.splice(1,0, 2,3)
// == [].splice.apply(items, [1, 0].concat([2, 3]));
// == items.splice(1, 0, ...items);
console.log(items); // [ 1, 2, 3, 4 ]
slice() vs splice()
많은 자바스크립트 개발자들이 slice()와 splice()를 헷갈려 하는 이유는 비단 이름이 비슷해서뿐만 아니라 실제로 동일한 목적을 위해 두 함수 중에 아무거나 사용해도 무방한 경우가 있기 때문이다. 예를 들어 아래 예시의 nums 배열에서 5부터 7까지를 복사한 값을 담고 있는 새로운 배열을 얻고 싶을 때 두 가지 방법을 다 쓸 수 있다.
let nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
nums.slice(5, 8); // [5, 6, 7]
nums.splice(5, 3); // [5, 6, 7]
차이점이라면 두 번째 인자로 slice()는 종료 인덱스를, splice()는 몇 개의 값을 떼어낼지 넘긴다는 것이다. 하지만 이 두 함수를 동일한 배열을 대상으로 여러 번 호출할 수 있는 상황이라면 얘기가 좀 달라진다.
let nums = Array(20).fill().map((_, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
nums.slice(5, 8) // slice() 1회 호출: [5, 6, 7]
nums.slice(5, 8) // slice() 2회 호출: [5, 6, 7]
nums.slice(5, 8) // slice() 3회 호출: [5, 6, 7]
nums.splice(5, 3) // splice() 1회 호출: [5, 6, 7]
nums.splice(5, 3) // splice() 2회 호출: [8, 9, 10]
nums.splice(5, 3) // splice() 3회 호출: [11, 12, 13]
slice()는 배열의 일부분을 복사해서 반환하며 원본을 훼손하지 않는 반면 splice()는 배열에서 요소를 제거하고 제거하고 제거한 위치에 다른 요소를 추가하며 원본을 훼손한다. 따라서 항상 같은 배열을 반환하는 slice()와 달리 splice() 함수는 계속해서 원본 배열을 깎아먹기 때문에 동일한 인자로 여러 번 호출했을 때 매번 다른 배열을 반환하게 된다.
'Javascript > Javascript' 카테고리의 다른 글
[Javascript] 이벤트 핸들러와 this (0) | 2024.10.16 |
---|---|
[Javascript] Event의 종류 (2) | 2024.10.15 |
[Javascript] Object 객체 함수 (0) | 2024.10.08 |
[Javascript] for ... in (0) | 2024.09.30 |
[Javascript] length 속성의 이해 (0) | 2024.09.30 |