JS. 배열

MJ·2022년 9월 25일
0

Java Script

목록 보기
21/57
post-thumbnail

배열

  • 순서있는 데이터들에 대한 집합체

  • key를 사용해서 식별할 수 있는 값을 담은 컬렉션은 객체라는 자료구조를 이용해 저장합니다.

  • 객체는 순서와 관련된 메서드가 없어서, 순서가 있는 컬렉션은 저장할 수 없습니다.
    자료구조 배열을 사용하면 순서가 있는 컬렉션을 저장할 수 있습니다.



1. 배열 선언

  • 두 가지 방법으로 배열을 생성할 수 있습니다.
let arr = new Array();
let arr = [];	// * 이 방식을 많이 사용합니다.

배열에는 초기값을 지정해줄 수 있고, 배열 요소는 숫자 '0'부터 시작합니다.
배열 요소에서 순서를 나타내는 숫자 값을 인덱스라고 부릅니다.
인덱스 값을 통해서 배열의 특정 부분만 가져올 수 있습니다.

let fruits = ['사과', '오렌지', '자두'];	// 배열에 3개의 변수를 추가

/* 배열의 특정 요소만 출력 */
alert( fruits[0] );	// '사과' , fruits 배열의 첫 번째 변수 
alert( fruits[1] );	// '오렌지', fruits 배열의 두 번째 변수 
alert( fruits[2] );	// '자두', fruits 배열의 세 번째 변수 


/* 배열의 특정 부분만 수정하기 */
fruits[2] = '배';	// fruits['사과', '오렌지', '배']로 변경됨


/* 새로운 배열 추가하기 */
fruits[3] ='포도';	// fruits['사과', '오렌지', '배', '포도']


/* 배열의 길이 확인하기 */
alert(fruits.length);	// 4


/* 배열 전체 출력 */
alert(fruits);		//'사과', '오렌지', '배', '포도'

배열에 들어가는 요소에는 자료형의 제약이 없습니다.

let arr = [ '사과', { name: '이보라' }, true, function() { alert('배열안의 함수'); } ];

alert( arr[1].name );	// '이보라', arr배열 안의 [1]번째 요소(객체)의 프로퍼티에 접근
arr[3]();	// '배열안의 함수', arr[3] 배열의 함수 호출


🔔 trailing 쉼표

배열의 끝을 나타내는 마지막 요소는 프로퍼티와 동일하게 ,쉼표를 사용합니다.



2. 배열 요소 접근

  • 배열에 요소에는 자료형의 제약이 없이 어떤 자료형이든 포함할 수 있습니다.

  • 요소중에 문자열이 오게 되면, 문자열 중에서도 일부만 호출해서 보여줄 수 있습니다.


2.1 배열안 요소 중 문자열에 접근하기

let userName = [ 'yeongmi', 'chulsu', 'daegon' ]

console.log(userName[0])	// 결과 : yeongmi
console.log(userName[0][0])	// 결과 : y

/*
배열안에 인덱스를 통해서 요소를 출력하고, 요소가 문자열로 구성되어 있으면
문자열의 인덱스를 통해서 특정 부분만 출력할수도 있습니다.
*/

2.2 배열안 요소 중 문자열 길이 확인하기

let userName = [ 'yeongmi', 'chulsu', 'daegon' ]

console.log(userName.length)	// 결과 : 3
console.log(userName[0].length)	// 결과 : 7
console.log(userName[1].length)	// 결과 : 6

2.3 배열안에 요소를 수정

let playerName = 'dongjun';
playerName[0] = 'c';	// 일반 변수에서는 인덱스를 통해 변경 불가능


let userName = [ 'yeongmi', 'chulsu', 'daegon' ]
userName[0] = 'kiyeong';	// 배열에서는 인덱스를 통해 특정 요소를 새로운 문자열로 수정할 수 있다. 


/* 
배열에서 인덱스에 접근해서 문자열을 수정하면, 기존의 문자열에서 수정되는 것이 아니라
새로운 문자열로 덮어씌워지는 것 입니다.
*/


console.log(userName.length);	// 결과 : 3
userName[10] = 'ET';		
console.log(userName.length);	// 결과 : 11
console.log(userName[10]);		// 결과 : ET
console.log(userName);			// 결과 : [ 'yeongmi', 'chulsu', 'daegon', <7 empty items>, 'ET' ]

/* 
기존의 userName 인덱스에서 [3]이 아닌 건너뛰어 [10]을 추가하게 되면
[2]-[10] 사이에 빈 공간으로 인덱스가 채워지게 됩니다.

공백 부분인 [3]-[9] 사이를 호출하면 할당되지 않았기에 undefined로 출력됩니다.
*/


userName[3] = 'DG';
console.log(userName[3]);	// 결과 : DG



3. Queue 자료구조

  • 큐(queue)는 배열을 사용해서 만들 수 있는 대표적인 자료구조 입니다.

  • 배열과 마찬가지로 순서가 있는 컬렉션을 저장하는 데 사용합니다. 큐의 주요 연산은 다음과 같다.

push
① 맨 끝에 요소를 추가합니다.

shift
① 제일 앞 요소를 꺼내서 제거합니다.
② 남아있는 요소들을 앞으로 밀어줍니다.
③ 결과적으로, 두 번째 요소가 첫 번째 요소가 됩니다.

배열엔 두 연산을 가능케 해주는 내장 메서드 pushpop이 있습니다.

🔔 큐(queue)의 사용 용도

실무에서 화면에 순차적으로 띄울 메세지를 비축해 놓을 자료구조를 만들 때 사용합니다.



4. Stack 자료구조

  • 큐와 마찬가지로 배열을 사용해서 만들 수 있는 자료구조 중 하나가 스택(stack)입니다.

  • 스택에서 사용하는 연산은 다음과 같습니다.

push
요소를 스택 끝에 집어 넣습니다.

pop
스택 끝의 요소를 출력합니다.

스택은 한쪽 끝에 요소를 더하거나 출력할 수 있습니다.
흔히 카드와 비교 되는데, 쌓여있는 카드 맨 위에 새로운 카드를 넣어주거나, 빼는 것과 같다.


⚠️ 큐와 스택의 차이점

큐는 처음에 집어넣은 요소가, 맨 처음으로 나오기에 선입선출 자료구조라고 부릅니다.
(First-In-Frist-Out, FIFO)

스택은 마지막에 집어넣은 요소가, 맨 처음으로 나오기에 후입선출 자료구조라고 부릅니다.
(Last-In-First-Out, LIFO)


이런 식으로 처음이나 끝에 요소를 추가하거나 빼주는 자료구조를 데큐라고 부릅니다.
(Double Ended Queue, DEQUE)



5. 배열 메서드 메서드

  • 배열에서 주로 사용되는 메서드에 대해 살펴보겠습니다.



5.1 pop

  • 배열 끝 요소를 제거하고, 제거한 요소를 반환 합니다.

  • 괄호에 인수가 필요하지 않습니다.

let fruits = ['사과', '오렌지', '포도'];

fruits.pop();				// '포도', 배열의 마지막 부분을 제거합니다.
console.log( fruits );		// '사과', '오렌지'

5.2 push

  • 배열 끝부분에 요소를 추가 합니다.

  • 괄호에 인수가 필요하다.

/* PUSH 메서드 사용 */
let fruits = ['사과', '오렌지'];

fruits.push('배');
console.log(fruits);	// '사과', '오렌지', '배'


/* length 메서드로도 인덱스 끝 부분에 추가할 수 있음 */
fruits[fruits.length] = '포도';
console.log(fruits);	// '사과', '오렌지', '배', '포도'


/* 한 번에 여러요소 추가 */
fruits.push('딸기', '키위');
console.log(fruits);	// '사과', '오렌지', '배', '포도', '딸기', '키위'


/*
`fruits.push()` 메서드를 호출하는 것은 `fruits[fruits.length] = ...`와 동일하다.
*/

5.3 shift

  • 배열 앞의 요소를 제거하고, 제거한 요소를 반환합니다.

  • 인수가 필요하지 않다.

let fruits = ['사과', '오렌지', '배'];

console.log(fruits.shift());	// '사과', 배열의 맨 앞 요소를 제거하고 출력
console.log(fruits);			// '오렌지', '배'

5.4 unshift

  • 배열 앞에 요소를 추가합니다.

  • 인수가 필요합니다.

let fruits = ['오렌지', '배'];

fruits.unshift('사과');	

console.log(fruits);	// 사과, 오렌지, 배


/* 한 번에 여러 요소 추가 */
fruits.unshift('참외', '메론');
console.log(fruits);	// 참외, 메론, 사과, 오렌지, 배



6. 배열의 내부 동작 원리

  • 배열은 특별한 종류의 객체입니다.

  • 배열 arr의 요소를 arr[0] 처럼 대괄호를 사용해서 접근 하는 방식은 객체의 문법에서
    유래 되었습니다.

  • 객체와 다른점은 배열은 키가 숫자라는 점입니다. arr 배열의 키는 [0], [1] 같은
    인덱스 숫자형 키를 사용함으로써 배열은 객체의 기본 기능 + 순서가 있는 컬렉션을
    제어하게 해주는 특별한 메서드를 제공합니다. length 프로퍼티도 제공합니다.

  • 배열은 JS의 7가지 원시 자료형에 속하지 않은 객체이기 때문에 객체처럼 동작합니다.
    즉, 배열 또한 참조를 통해서 복사될 수 있습니다.

let fruits = ['바나나'];

let arr = fruits;	// fruits가 참조하는 객체(배열)를 복사함

alert( arr == fruits );	// true

arr.push('배');	// 참조값을 이용해서 객체를 수정

alert( fruits );	// 바나나, 배

6.1 배열 내부 표현 방식

  • 배열을 배열답게 만들어주는 것은 특수 내부 표현방식입니다.

  • JS 엔진은 배열의 요소를 인접한 메모리 공간에 차례대로 저장해 연산 속도를 높입니다.

  • 개발자가 배열을 순서가 있는 컬렉션으로 다루지 않고, 일반 객체 처럼 순서가 없는
    컬렉션으로 다루게 되면 최적화 기법들이 제대로 동작하지 않습니다.

let fruits = [];	// 빈 배열
	
fruits[99999] = 5;	// 99999 프로퍼티에 5의 값을 할당

fruits.age = 25;	// 'age' 프로퍼티에 25 값을 할당 


/* 위 배열의 구조는 아래와 같다 */
fruts[] {
 99999: 5,
 age: 25,
}

배열은 객체입니다. 위와 같이 배열보다 긴 arr[99999] 같은 프로터피를 생성해도 문제가
발생하지 않습니다. 다만 이런식으로 작성하면 JS엔진이 배열을 일반 객체처럼 다루기에
배열을 다룰 때 발생하는 최적화 기법이 동작하지 않아 배열 특유의 장점이 사라집니다.

⚠️ 배열의 잘못된 방법

arr.test = 5 프로퍼티의 키를 숫자가 아닌 다른 자료형으로 사용하는 경우
arr[0]arr[100]만 추가하고, 그 사이에 아무런 요소도 없는 경우
arr[100] arr[99]같이 역순으로 배열을 추가하는 경우


배열은 순서가 있는 자료형을 저장하는 용도로 만들어진 특수한 자료구조 입니다.
배열을 사용하는 메서드들은 이런 용도에 맞게 만들어져 있습니다.
프로퍼티가 숫자가 아닌, 다른 자료형 또는 숫자더라도 순서대로 요소를 채우는 방법이
아니라면 배열 대신 일반 객체 { }를 사용하는게 바람직합니다.



7. 성능

  • 후입 선출의 메서드 push pop는 속도가 빠릅니다.

  • 선입 선출의 메서드 shift unshift는 속도가 느립니다.


7.1 선입 선출이 느린 이유

fruits.shift();	// 배열 맨 앞의 요소를 빼냅니다.


/* shift 메서드 연산 과정 */
1) 배열의 맨 앞, 인덱스가 [0]인 요소를 제거
2) 모든 요소를 왼쪽으로 이동 시킵니다. 인덱스[1][0]이 되고, [2][1]이 된다.
3) length 프로퍼티 값을 갱신 합니다.

위와 같이, 배열의 요소가 많아질수록 연산하는 시간이 길어지게 됩니다.
shift 메서드 뿐만 아니라, unshfit 또한 유사하게 동작합니다. 요소를 배열 맨 앞에 추가
하려면, 기존에 있던 요소들을 우측으로 밀어내고, length 프로퍼티 값을 재 갱신해야 하므로,
동일하게 연산 처리 과정에 시간이 걸리게 됩니다.


7.2 후입 선출이 빠른 이유

후입 선출은, 요소를 추가하거나 빼더라도 기존의 요소를 이동시키는 과정이 없기 때문에
선입 선출보다 연산 속도가 빠릅니다.

단순하게 배열의 끝 부분에 요소를 추가 & 제거하고, length 프로퍼티 값만 갱신하면 됩니다.

fruits.pop()	// 배열 끝의 요소를 하나 빼냅니다.

pop push 메서드는 기존의 요소를 이동시키지 않기에 선입 선출보다 연산속도가 빠릅니다.



8. 반복문

  • for 문을 사용해서 객체의 프로퍼티를 순회하는 것 처럼 배열 또한 순회할 수 있습니다.
let arr = ['사과', '오렌지', '배'];

for( let i = 0; i < arr.length; i++ ){
 alert( arr[i] );	// arr[0] ~ arr[2] 출력 
}


/* 배열을 순회하는 또 다른 방법 for of 문 */


let fruits = ['사과', '오렌지', '배'];

for( let fruit of fruits ) {
 alert( fruit );	// arr[0] ~ arr[2] 출력 
}


/* */
for of 문은 출력되는 요소의 인덱스 값은 얻을 수 없고, 인덱스에 해당되는 값만 얻을 수 있습니다.
객체로 따지면, 프로퍼티의 키는 알 수 없고 값만 얻을 수 있습니다.

출력 결과는 위의 for문과 동일하므로 배열의 요소를 대상으로 반복적인 작업을 수행하는 경우
간단하게 for of 문을 사용할 수 있습니다.

배열은 객체에 속하므로, for..in 반복문도 사용할 수 있습니다.

let arr = ['사과', '오렌지', '배'];

for(let key in arr) {
  alert( arr[key] ); // 사과, 오렌지, 배 
}


/* */
❗ 배열에서는 for..in 반복문을 권장하지 않습니다.

⚠️ 배열에서 for..in 반복문을 권장하지 않는 이유

1) for..in 반복문은 모든 프로퍼티를 대상으로 순회하므로, 프로퍼티의 키가
숫자가 아닌 자료형도 포함됩니다. 브라우저나 기타 호스트 환경에서 쓰이는 객체 중
배열과 유사한 형태를 보이는 유사 배열(array-like)가 있습니다.

유사 배열 객체는 일반 배열과는 달리 키가 숫자형이 아닌 프로퍼티와 메서드가 있을 수
있습니다. 유사 배열 객체와 for..in 반복문을 사용하면 모든것을 대상으로 순회
하므로, 필요 없는 프로퍼티들이 문제를 일으킬 수 있습니다.

2) for..in 반복문은 객체와 함께 사용할 떄 최적화되어 있기에 배열과 사용하면
객체 대비 수십배이상 속도가 느립니다.



9. length 프로퍼티

  • 배열은 무언가 이벤트가 발생하면 length 프로퍼티가 자동으로 갱신됩니다.
    이 프로퍼티는 배열 내 요소의 개수가 아니라 가장 큰 인덱스에 1을 더한 값 입니다.

  • 즉 배열의 요소가 1개가 있더라도, 그 요소가 매우 큰 정수라면 인덱스도 매우 커지겟죠

let fruits = [];	// 빈 배열 
fruits[123] = '사과';	

alert( fruits.length );	// 124


/* */
length 원리를 보여주기 위해서 위함이므로 배열은 순서가 있는
자료를 대상으로 사용합시다.    

length의 다른 특징으로는 값을 할당할 수 있다는 것 입니다.
값을 수동으로 증가시키면 아무런 일도 발생하지 않지만, 값을 감소시키면 배열이 잘립니다.
짧아진 배열은 원래대로 되돌릴 수 없습니다.

let arr = [1, 2, 3, 4, 5];

arr.length = 2;	// 배열의 길이를 2로 감소시킴.
alert( arr );		// [1, 2]

arr.length = 5;	// 배열의 길이를 5로 증가시킴.
alert( arr );		// 1,2...., 삭제된 기존 요소들은 복구되지 않는다.


/* */
이러한 특징을 이용해서 배열을 비울 수 있습니다.
arr.length = 0;



10. new Array()

  • 배열을 만드는 2번째 방식입니다.
let arr = new Array('사과', '오렌지', '배');	

  • [] 대괄호를 사용하면 간단하게 배열을 만들 수 있어 잘 사용하지 않는 방식입니다.
  • 다만 인수에 숫자를 하나만 넣으면 이 배열은, 요소가 없는 반면에 길이는 인수와 같아집니다.
let arr = new Array(2);	// 인수로 숫자 2만 전달

alert( arr[0] );		// undefined, 요소가 없다.
alert( arr.length );	// 2, 길이는 2입니다

⚠️ 배열을 만들 때는 [] 대괄호를 사용하자.

위 예시처럼 new array(number)로 만든 배열은 요소가 없어 undefined 상태입니다.
이런 예외 상황이 발생할 수 있어 배열을 생성할 때는 대괄호를 권장합니다.



11. 다차원 배열

  • 배열 역시 배열의 요소가 될 수 있습니다. 이런 배열을 다차원 배열이라고 부릅니다.

  • 다차원 배열은 행렬을 저장하는 용도로 사용합니다. (multidimensional array)

let matrix = [
  [1, 2, 3],	// [0] [0,1,2]
  [4, 5, 6],	// [1] [0,1,2]
  [7, 8, 9],	// [2] [0,1,2]
 ];

alert( matrix[1][1] );	// 5

12. 배열과 toString()

  • 배열에는 toString() 메서드가 구현되어 있어서 이를 호출하면 요소를 쉼표로 구분한
    문자열이 반환
    됩니다.
let arr = [1, 2, 3];	// 배열에 정수 1 2 3 저장

alert( arr );	//	1, 2, 3
alert( String(arr) == '1, 2, 3' );	// true, 배열을 문자형으로 형 변환
alert( arr == '1,2,3' );			// true 
// 배열엔 toString() 메서드가 구현되어 있기에 배열을 호출하면, 배열안의 요소들을
// 쉼표로 구분해서 문자열로 반환한다.

배열은 Symbol.toPrimitive 또는 valueOf 메서드가 없습니다. 따라서 배열을 원시형으로
형 변환하는 과정이 발생하면, 문자열로 병합됩니다.

alert( [] + 1 ); 같은 경우에도 hintdefault가 아닌 String이 됩니다.

alert( [] + 1 );	// '1'	, '' + 1
alert( [1] + 1 );	// '11'	, '1' + 1
alert( [1,2] + 1);	// '1,21', '1, 2' + 1


/* */
위 예시에선 [] 빈 배열이 빈 문자열로 형 변환됩니다.
즉 모든 연산은 '' 빈 문자열과 병합된다고

마지막 예시 [1,2] + 1 같은 경우에는, 배열에는 toString() 메서드로 요소들이
쉼표로 구분된 문자열로 반환 됩니다.

[0]번째 요소 1[1]번째 요소 2'1, 2'인문자열로 반환됨
'1, 2' + 1, 문자열 '1, 2'1을 문자열로 병합한다.
결과적으로 '1, 21'이 됩니다.



정리

배열은 특수한 형태의 객체로, 순서가 있는 자료를 저장하고 관리하는 용도로 쓰입니다.
선언 방법에는 [] 대괄호와 new Array() 함수 방식이 있습니다.

let arr = [1, 2, 3]
let arr2 = new Array(1, 2, 3);

new Array(number) 배열을 선언할 때 인수로 숫자 하나를 넘기면, 요소는 없지만 길이가
number만큼 할당 됩니다. 5를 인수로 넘기면 생성된 배열의 length는 5가 됩니다.


length 프로퍼티는 배열의 길이를 나타냅니다. 정확히는 배열의 가장 큰 인덱스 + 1 입니다.
배열 메서드는 length 프로퍼티를 자동으로 조정해줍니다.
length 값을 감소시키면 나머지 배열들은 짤립니다. ( 복구 불가능 )


다음 연산을 사용해서 배열을 데큐처럼 사용할 수 있습니다.

push(..items)
items을 배열 끝에 추가합니다.

pop()
배열 끝의 요소를 제거하고, 제거된 요소를 출력합니다.

shift()
배열의 처음 요소를 제거하고, 그 요소를 출력합니다.

unshift(...itesm)
배열의 처음에 items을 추가합니다.


배열을 대상으로 반복적인 작업을 할 때 사용되는 방법이 3가지 있습니다.

for(let i=0; i < arr.length; i++)
가장 빠른 방법이고, 오래된 브라우저와도 호환됩니다.

for(let item of arr)
모던한 JS에서 배열을 대상으로 자주 사용합니다.

for(let item in arr)
객체를 위해서 만들어진 방식으로, 배열에 사용하는 것은 권장하지 않습니다.

profile
프론트엔드 개발자가 되기 위한 학습 과정을 정리하는 블로그

0개의 댓글