티스토리 뷰

 

▶배열

자바스크립트의 배열은 개체의 특별한 형태로 순서가 있는 데이터의 집합입니다.

배열 안의 값은 원소라고 하며, 배열의 위치를 가리키는 인덱스로 각 원소에 접근합니다.

인덱스는 0부터 시작하는 정수이며, 배열 값은 어떤 타입의 데이터든 사용할 수 있습니다.

배열은 객체이지만 정수 타입인 인덱스를 프로퍼티로 갖는 특별한 데이터입니다.

 

1) 배열의 생성

▷Array() 생성자 사용

객체와 마찬가지로 배열도 자바스크립트에 내장된 생성자 함수 Array()가 있으며, 이를 사용해 새로운 배열을 생성할 수 있습니다.

const arr = new Array(1, '1', true);

console.log(arr) // [1, '2', true]

Array() 생성자 함수는 새로운 배열을 생성하고 인자로 받은 값들을 배열 원소로 채워 넣어 초기화 합니다.

위 코드에서 1, 2, 3 3개의 인자를 배열 원소로 초기화했습니다.

만약 생성자 함수의 인자가 1개이고 숫자 값이라면 해당 값을 배열의 length 프로퍼티에 할당해 새로운 배열을 생성합니다.

const arr = new Array(3);
// 길이가 3인 빈 배열이 생성됩니다.
console.log(arr); // [empty, empty, empty]

하지만 Array() 생성자 함수 역시 배열 리터럴 문법이 더 간단하고 명확해 거의 사용되지 않습니다.

 

▷배열 리터럴

배열 리터럴은 대괄호([])를 사용합니다.

const arr = [1, '2', true];

코드를 보면 알 수 있듯이 배열 리터럴은 Array() 생성자 함수보다 훨씬 간단하게 배열을 생성하고 초기화합니다.

 

혹시 위 코드에서 특이한 점을 발견했나요?

배열 원소 타입으로 숫자, 문자열 그리고 불리언을 볼 수있습니다.

이 타입들뿐만 아니라 객체, 배열 등 모든 타입의 값을 원소로 사용할 수 있습니다.

이런 배열을 비균질적 배열이라고 합니다.

하지만 이런 배열 데이터는 일관성 있게 처리하기 힘들고 다른 개발자가 어떤한 데이터인지 파악하기도 어렵습니다.

자바스크립트 배열이 비균질적인 특성을 가진다는 것만 이해하고 실제 배열 데이터를 다룰 때는 가급적 통일된 타입의 데이터를 사용하는 것이 좋습니다.

 

2) 원소 접근과 동적인 원소 생성

배열 원소는 대괄호([]) 안에 인덱스값을 넣어 접근합니다.

만약 배열 길이보다 큰 인덱스값을 넣어 접근한다면 값이 할당되지 않음을 표현하는 undefined 값을 반환합니다.

정의되지 않은 프로퍼티 명(배열의 길이보다 큰 인덱스)으로 객체에 접근한 것돠 동일하다고 생각하면 쉽게 이해될 것입니다.

const arr = ['f', 'o'. 'o'];

console.log(arr[0]); // 'f'
console.log(arr[1]); // 'o'
console.log(arr[2]); // 'o'
console.log(arr[3]); // undefined

좀 더 객체의 관점에서 배열을 생각해 볼까요?

우리는 앞서 객체에서 동적으로 프로퍼티를 추가할 수 있다고 살펴보았습니다.

따라서 배열도 객체이기 때문에 동적으로 배열 원소를 추가할 수 있습니다.

특히 자바스크립트의 배열은 순차적으로 값을 넣을 필요 없이 아무 인덱스 위치에나 값을 동적으로 추가할 수 있습니다.

이때 값을 추가한 인덱스의 위치에 따라 length 프로퍼티도 갱신됩니다.

자바스크립트 배열의 length 프로퍼티는 배열의 최대 인덱스보다 항상 크기 때문입니다.

const arr = [];

arr[0] = 1;
arr[2] = 3;

console.log(arr); // [1, empty, 3]
consold.log(arr.length); // 3

코드의 4번재 줄을 보면 인덱스 1을 건너뛰고 2에 원소를 추가했습니다.

이때 배열의 최대 인덱스는 2이고 그 인덱스를 기준으로 length 프로퍼티가 갱신됩니다.

 

배열 역시 일반 객체처럼 이름:값 형태의 데이터도 추가할 수 있습니다. 다만, 이때 배열의 length 프로퍼티에는 변화가 없으니 주의해야 합니다.

const arr = [];

arr.foo = 'foo';

console.log(arr.length); // 0

하지만 배열에 이름:값 형태의 데이터를 추가하는 것은 권장하지 않습니다.

위 코드에서 볼 수 있듯이 length 프로퍼티는 갱신되지 않고 프로퍼티만 추가됩니다.

또한 이러한 프로퍼티는 일반 배열 원소처럼 다룰 수 없어 배열을 순회하거나 정렬하는 내장 메서드와 함께 사용할 수 없습니다.

만약 꼭 이렇게 사용해야 한다면 그냥 일반 객체를 사용하고 배열에선 정수 형태의 인덱스만 프로퍼티로 사용하는 것이 혼란을 막는 방법입니다.

 

3) 희소 행렬

앞서 봤던 배열 원소 할당 코드를 다시 보겠습니다.

const arr = [];

arr[0] = 1;
arr[2] = 3;

console.log(arr); // [1, empty, 3]
consold.log(arr.length); // 3

인덱스 0과 인덱스 2에만 원소를 할당했고, 인덱스 1에는 어떠한 값도 할당되지 않아 빈 상태로 표시됩니다.

이 빈 상태는 명시적으로 배열의 원소에 undefined를 할당한 것과는 다릅니다.

이처럼 배열 원소가 연속적이지 않고 중간에 빈 배열을 희소 원소라고 합니다.

희소 배열의 빈 원소는 forEach(), map(), filter()와 같은 배열의 내장 메서드에서 무시됩니다.

반면 find()나 findIndex() 메서드는 빈 원소를 무시하지 않고 모두 탐색합니다.

이처럼 희소 배열은 어떠한 메서드를 사용하는가에 따라 일관적이지 않은 동작을 수행하며, 코드 가독성이나 데이터 구조 파악에도 좋지 않으니 특수한 경우가 아니라면 사용을 지양해야 합니다.

 

4) length 프로퍼티

 자바스크립트 배열의 length 프로퍼티는 배열의 길이를 반환합니다.

length 프로퍼티는 부호 없는 32비트 정수 데이터만 사용하기 때문에 2^32-1 이하의 정수만 설정할 수 있스니다.

또한 배열의 최대 인덱스보다 큽니다.

const arr = [];

arr[0] = 1;
arr[2] = 3;

console.log(arr); // [1, empty, 3]
consold.log(arr.length); // 3

중간값이 빈 희소 배열이지만 최대 인덱스를 기준으로 length 프로퍼티 값을 계산해 최대 인덱스 2에 1을 더한 3을 배열의 길이로 반환합니다.

도한 이 코드를 보면 배열의 원소에 값을 직접 할당하면 length 프로퍼티도 갱신된다는 것을 알 수 있습니다.

배열 조작 메서드를 호출하는 경우도 마찬가지입니다.

ㅊconst arr = [];
console.log(arr.length); // 0
// push 메서드는 배열의 맨 끝 인덱스에 새로운 원소를 추가합니다.
arr.push(1);
// length 값이 1로 갱신되었음을 알 수 있습니다.
console.log(arr.length); // 1

length 프로퍼티를 직접 수정해 배열의 길이를 늘리거나 줄일수도 있습니다.

이때 수정한 배열 길이에 맞게 배열의 원소도 추가(빈 원소)되거나 삭제됩니다.

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

length 프로퍼티는 배열 데이터를 순회하거나 조작할 대 매우 중요합니다.

적어도 위에서 설명한 특징은 명확히 이해하고 사용해야 합니다.

 

5) 배열 조작

배열은 원소를 조작할 수 있는 내장 메서드를 갖습니다.

배열 조작 메서드는 배열의 원본 데이터를 직접 수정하거나 메서드와 기존 배열 데이터를 기반으로 조작해 새로운 배열을 생성하는 메서드 두 가지로 나뉩니다.

[NOTE]

정확히는 Array.prototype의 메서드를 상속받아 사용합니다.

아직 프로토타입에 대해 살펴보지 않아 이해하기 어렵다면 배열이 Array.prototype의 메서드와 프로퍼티를 상속받아 사용한다고 생각하면 이해에 도움이 될 것입니다.

▷원본 배열 데이터 수정 메서드

다음 메서드들은 원본 배열의 데이터를 수정하며 length 프로퍼티도 함께 갱신합니다.

  • shift()

배열의 첫 번째 원소를 삭제하고, 결과값으로 삭제된 원소를 반환합니다.

const arr = [1, 2];

console.log(arr.shift()); // 1
console.log(arr); // 2
  • unshift()

배열의 첫 번째 인덱스를 원소에 추가하고, 결과값으로 배열의 새로운 길이를 반환합니다.

const arr = [1, 2];

console.log(arr.unshift(-1, 0)); // 4 (length 반환)
console.log(arr) // [-1, 0, 1, 2]
  • push()

배열의 마지막 인덱스에 하나 이상의 원소를 추가하고, 결과값으로 배열의 새로운 길이를 반환합니다.

const arr = [1];

console.log(arr.push(2, 3)); // 3
console.log(arr); // [1, 2, 3]
  • poo()

배열에서 마지막 인덱스에 해당하는 원소를 삭제하고 그 값을 결과값으로 반환합니다.

const arr = [1, 2];

console.log(arr.pop()); // 2
console.log(arr); // [1]
  • splice()

배열의 원소를 추가하거나 교체 또는 삭제해 배열 데이터를 변경합니다. 결과값으로 삭제된 원소의 배열을 반환합니다.

const arr = [1, 2, 3];

console.log(arr.splice(1, 2)); // [2, 3]
console.log(arr); // [1]
  • sort()

배열의 원소를 인자로 넘긴 비교 함수를 사용해 정렬합니다. 비교 함수를 생략할 때 각 문자의 유니코드 포인트 값에 따라 정렬됩니다. 단, 숫자도 문자로 변환하여 정렬한다는 것을 유의해야 합니다. 문자 정렬 방식이 아닌 별도의 비교 방식을 정의하고 싶다면 비교 함수를 정의해 전달하여 사용합니다.

const arr = [3, 2, 4, 21, 55];
arr.sort();

console.log(arr); // [2, 21, 3, 4, 55]

 

▷새로운 배열 생성 메서드

다음 메서드들은 원본 배열의 데이터와 length 프로퍼티에는 영향을 미치지 않고 새로운 배열을 생성합니다.

  • concat()

인자로 넘긴 배열 또는 값들을 기존 배열에 합쳐 새로운 배열을 생성해 반환합니다.

const arr = [1, 2, 3];
const newArr = arr.concat([4, 5]);

console.log(arr); // [1, 2, 3]
console.log(newArr); // [1, 2, 3, 4, 5]
  • slice()

배열에서 특정 범위의 원소를 복사해 새로운 배열을 생성해 반환합니다. 단, 얕은 복사를 수행하기 때문에 배열의 원소가 객체이면 참조가 유지되니 주의해야 합니다.

[NOTE]

얕은 복사(Shallow Copy)는 대상 객체를 새로 생성하지만 내부에 중첩된 객체는 새로 생성하지 않고 동일한 객체를 참조합니다.

반대로 중첩된 객체까지 모두 새로 생성하는 복사를 깊은 복사(Deep Copy)라고 합니다.

const obj = {};
const arr = [1, obj, 3];
const newArr = arr.slice(1, 2);
// slice() 메서드는 얕은 복사를 하기 대문에 배열 내에 줍첩된 객체의 참조가 유지됩니다.
console.log(newArr[0] == obj); // true

배열을 복사하는 또 다른 방법은 펼침 연산자(Spread Operator)를 사용하는 것입니다. slice() 메서드와 마찬가지로 얕은 복사를 수행합니다.

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

 

▷map(), forEach, filter()

map(), forEach(), filter()는 배열을 다룰 때 굉장히 많이 사용하는 메서드들입니다.

이 메서드들은 익명 함수를 사용해 간결하게 배열의 원소를 다룰 수 있습니다.

[NOTE]

익명함수는 말 그래도 이름이 없는 함수입니다. 주로 콜백 함수 또는 함수 표현식을 정의할 때 사용합니다.

  • map()

배열의 모든 원소를 인자로 받은 함수를 실행해 특정한 형식으로 변경합니다.

변경한 원소들로 새로운 배열을 생성해 반환합니다.

const arr = [1, 2, 3];
const newArr = arr.map(x => x + 1);

console.log(newArr); // [2, 3, 4]
  • forEach()

인자로 받은 함수를 배열의 모든 원소를 대상으로 실행합니다.

const arr = [1, 2, 3];
const newArr = arr.forEach(x >= console.log(x));
  • filter()

인자로 받은 함수의 테스트를 통과하는 원소들로 새로운 배열을 생성해 반환합니다.

const arr = [1, 2, 3];
const newArr = arr.filter(x >= x !== 1);

console.log(newArr); // [2, 3]

 

6) 유사 배열 객체

자바스크립트에서는 일반 객체를 배열처럼 사용할 수 있습니다.

이러한 객체를 유사 배열 객체라고 하며 유사 배열 객체는 length 프로퍼티로 양의 정수 값을 가진 객체여야만 합니다.

const arr = {
	0: 'Hi',
   	1: 'My',
   	2: 'name is',
   	3: 'javascript',
   	length: 4
};

유사 배열 객체의 대표적인 에는 자바스크립트 함수의 arguments 객체입니다.

arguments 객체는 함수에 전달한 인자를 유사 배열 객체로 만든 데이터입니다.

function foo(a, b, c) {
	console.log(arguments[0], arguments[1], arguments[2])}; // 'a', 'b', 'c'
   	console.log(arguments.length); // 3
}
foo('a', 'b', 'c');

arguments 객체는 마치 배열처럼 인덱스로 프로퍼티에 접근할 수 있으며, length 프로퍼티를 가집니다.

하지만 배열이 아닌 유사 배열 객체이기 때문에 배열의 내장 메서드를 사용할 수 없습니다.

function foo(a, b, c) {
	// 아래 코드는 TypeError가 발생합니다.
   	arguments.forEach((arr) => {
   		console.log(arr);
    });
}
foo('a', 'b', 'c');

이럴 때 배열의 내장 메서드를 call() 또는 apply() 함수와 결합해 사용합니다.

function foo(a, b, c) {
	// a, b, c가 순차적으로 출력되는 것을 볼 수 있습니다.
   	Array.property.forEach.call(arguments, (arr) => {
    	console.log(arg);
    });
}
foo('a', 'b', 'c');

 

 


여기까지 자바스크립트 배열에 대해 알아보았습니다 :)

728x90
LIST
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함