배열

김하은·2023년 5월 14일
0

키를 사용해 식별할 수 있는 값을 담음 컬렉션은 객체 라는 자료구조를 이용해 저장한다.
이러한 객체 만을 이용하여도 다양한 작업을 할 수 있다.

그러나 개발을 진행하다보면 첫번째요소, 두번째 요소, 세번째 요소 등과 같이 순서가 있는 컬렉션이 필요할 때가 생길때가 있다.

순서가 있는 컬렉션을 다루어야 할 때 객체를 사용한다면 순서와 관련된 메서드가 없기에 불편하다.
객체는 처음부터 순서를 고려하지 않고 만들어진 자료구조이기 때문에 객체를 이용하면 새로운 프로퍼티를 기존 푸로퍼티 사이에 끼워넣는 것도 불가능하다.

이럴때는 배열을 사용한다.

배열 선언

let arr = new Array(); 
let arr = [];

이렇게 두가지 방법으로 빈 배열을 만들 수 있다.

대부분 두번째 방법을 많이 사용한다.
이때 대괄호 안에 초기 요소들을 넣어줄 수도 있다.

배열의 각 요소에는 0 부터 시작하는 인덱스가 있다. 이 숫자들은 배열 내 순서를 나타낸다.
배열 내 특정 요소를 얻고자 한다면 대괄호 안에 이 순서를(인덱스를) 넣어주면 된다.

let fruits = ["사과", "오렌지", "자두"];

alert( fruits[0] ); // 사과
alert( fruits[1] ); // 오렌지
alert( fruits[2] ); // 자두

같은 방법을 사용해 요소를 수정할 수도 있다.\

또한 새로운 요소를 배열에 추가할 수도 있다.

length를 사용하면 배열에 담긴 요소가 몇개인지 알아낼 수도 있다.
배열이 담긴 변수를 alert에 넣어주면 요소전체를 텍스트로 얻을 수 있다.

alert( fruits ); // 사과,오렌지,자두

console.log나 다른 방법으로 변수를 사용할 때에는 배열을 받아볼 수 있다.

console.log(fruits)
(3) ['사과', '오렌지', '자두']

배열 요소의 자료형에는 제약이 없다.
문자, 숫자뿐만 아니라 객체, 심지어 함수도 들어갈 수 있다.

// 요소에 여러 가지 자료형이 섞여 있습니다.
let arr = [ '사과', { name: '이보라' }, true, function() { alert('안녕하세요.'); } ];

// 인덱스가 1인 요소(객체)의 name 프로퍼티를 출력합니다.
alert( arr[1].name ); // 이보라

// 인덱스가 3인 요소(함수)를 실행합니다.
arr[3](); // 안녕하세요.

배열의 마지막 요소는 객체와 마찬가지로 쉼표로 끝날 수 있다.
이 쉼표를 길게 늘어지는 쉼표라고 하여 trailing쉼표라고한다.
이렇게 마지막요소에도 쉼표를 사용한다면 모든줄의 생김새가 유사해지기때문에 요소를 넣거나빼는것이 가능해진다.

pop, push, shift, unshift

queue는 배열을 사용해 만들 수 있는 대표적인 자료구조이다. 배열과 마찬가지로 순서가 있는 컬렉션을 저장하는데 사용한다.
큐에서 사용하는 주요 연산

  • push -맨끝에 요소를 추가
  • shift - 제일 앞의 요소를 제거한뒤 나머지 요소들을 앞으로 밀어줌.

배열에는 두 연산을 가능하게 해주는 내장메서드 push 와 pop이 있다.

화면에 순차적으로 띄울 메세지를 비축해 놓을 자료구조를 만들 때 큐를 사용하는 것처럼 큐는 실무에서 자주 쓰이는 자료구죠이다.
배열은 큐 외에 stack이라고 불리는 자료구조를 구현시에도 사용된다.

스텍에서 사용하는 주요 연산

  • push - 요소를 스택 끝에 넣음
  • pop - 스택 끝 요소를 추출

스택은 이렇게 한쪽 끝 요소를 더하거나 뺄 수 있게 하는 자료구조이다.

스택을 사용하면 가장 나중에 넣은 요소가 먼저 나온다. 이러한 특징을 후입선출(Last-In-First-Out, LIFO)라고 한다.
반면 큐를 사용하면 먼저 넣은 요소가 먼저 나오기에 선입선출 (First-In-First-Out, FIFO) 자료구조라고 부른다.

자바스크립트 배열을 사용하면 큐와 스택 둘 다 다룰 수 있다.
이 자료구조들은 배열의 처름이나 끝에 요소를 추가하거나 빼는 데 사용된다.

이렇게 처음이나 끝에 요소를 더하거나 빼주는 연산을 제공하는 자료구조를 컴퓨터 과학 분야에서는 데큐 라고 부른다.

  • unshift - 배열 맨 앞에 요소를 추가한다.

배열의 내부 동작 원리

배열은 특별한 종류의 객체이다.
배열 arr의 요소를 arr[0]처럼 대괄호를 사용해 접근하는 방식은 객체 문법에서 왔다.
다만 배열은 키가 숫자라는 점에서 차이가 있는 것이다.

숫자형키(인덱스를 의미)를 사용함으로써 배열은 객체 기본기능 외에도 순서가 있는 컬렉션을 제어하게 해주는 특별한 베서드를 제공한다.
length라는 프로퍼티도 제공한다.
(** 배열의 본질은 객체!!)

이렇게 배열은 자바스크립트의 일곱가지 원시 자료형에 해당하지 않고 객체형에 속하기에 객체처럼 동작한다.

(** 배열은 객체와 마찬가지로 참조를 통해 복사된다.)
배열을 배열답게 만들어주는 것은 특수 내부 표현방식이다. 자바스크립트엔진은 배열의 요소를 인접한 메모리 공간에 차례로 저장해 연산 속도를 높인다.
이 방법 외에도 배열 관련 연산을 더 빠르게 해주는 최적화 기법은 많다.

개발자가 배열을 '순서가 있는 자료의 컬렉션'처럼 다루지 않고 일반 객체처럼 다루면 이러한 기법들이 제대로 동작하지 않는다.

let fruits = []; // 빈 배열을 하나 만듭니다.

fruits[99999] = 5; // 배열의 길이보다 훨씬 큰 숫자를 사용해 프로퍼티를 만듭니다.

fruits.age = 25; // 임의의 이름을 사용해 프로퍼티를 만듭니다.

배열은 객체이기에 이렇게 원하는 프로퍼티를 추가해도 문제가 되지 않는다.
그렇지만 이렇게 작성하게되면 자바스크립트 엔진이 배열을 일반 객체처럼 다루게 되어 배열을 다룰때만 적용되는 최적화 기법이 동작하지 않기에 배열 특유의 이점이 사라진다.

잘못된 방법
>
- arr.test = 5 같이 숫자가 아닌 값을 프로퍼티 키로 사용하는 경우
- arr[0]과 arr[1000]만 추가하고 그사이에 아무런 요소도 없는 경우
- arr[1000], arr[999]같이 요소를 역순으로 채우는 경우






**배열은 순서가 있는 자료를 저장하는 용도**로 만들어진 특수한 자료구조이다.
배열 내장 메서드들은 이러한 용도에 맞게 만들어졌따. 자바스크립트 엔진은 이러한 특성을 고려하여 배열을 신중하게 조정하고, 처리하기에 배열을 사용할 땐 이러한 목적에 맞게 사용해야한다.
임의의 키를 사용해야 한다면 배열보다는 일반 객체가 적합한 자료구조일 확률이 높다.



#### 성능

push 와 pop은 빠르지만 shift와 unshift는 느리다.

배열앞에 무언가를 해주는 메서드가 배열 끝에 무언가를 해주는 메서드보다 느린 이유를 살펴보자.
shift메서드(배열 맨 앞의 요소를 빼줌)를 호출한 것과 동일한 효과를 보려면 인덱스가 0인 요소를 제거하는것만으로는 충분하지 않다. 제거 대상이 아닌 나머지 요소들의 인덱스도 수정해 주어야 하기 때문이다.
그리고 length프로퍼티의 값을 갱신해야한다.

배열의 요소가 많으면 요소가 이동하는 데 걸리는 시간이 길고 메모리 관련 연산도 많아진다.

unshift 를 실행했을때에도 이와같은 유사한 일이 일어난다.

그러면 pop()과 push()는??
이것들은 요소의 이동을 수반하지 않는다. pop으로 마지막 요소를 제거하고 length 프로퍼티의 값을 줄여주기만 하면 된다.
따라서 배열끝에 무언가를 해주는 메서드의 실행 속도가 빠른것이다.\

#### 반복문
for문은 배열을 순회할 때 사용하는 가장 오래된 방법이다. 순회시에는 인덱스를 사용한다.

배열에 적용할 수 있는 또 다른 순회 문법으로는 for...of가 있다.

```js
let fruits = ["사과", "오렌지", "자두"];

// 배열 요소를 대상으로 반복 작업을 수행합니다.
for (let fruit of fruits) {
  alert( fruit );
}

이것을 사용하면 현재 요소의 인덱스는 얻을 수 없고 값만 얻을 수 있다.
원하는것을 충분히 구현할 수 있고, 문법도 짧기에 배열의 요소를 대상으로 반복작업을 할때에는 for...of문을 이용하는것도 좋다.

배열은 객체형에 속하기에 객체에서 사용되는 for...in을 사용할 수도 있다.

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

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

그러나 for...in을 사용할때 문제가 있다.

  • for..in 반복문은 모든 프로퍼티를 대상으로 순회합니다. 키가 숫자가 아닌 프로퍼티도 순회 대상에 포함됩니다.
    브라우저나 기타 호스트 환경에서 쓰이는 객체 중 , 배열과 유사한 형태를 가지는 '유사배열' 객체가 있다. 이것에는 length프로퍼티도 있고 요소마다 인덱스도 붙어있다.
    그런데 유사배열 객체에는 배열과는 달리 키가 숫자형이 아닌 프로퍼티와 메서드가 있을 수 있다.
    유사배열 객체와 for...in을 같이 사용하면 이 모든것을 대상으로 순회가 이루어질 수 있어 필요없는 프로퍼티들이 문제를 일으킬 가능성이 생긴다.

  • for...in반복문은 배열이 아니라 객체와 함께 사용할 때에 최적화되어 있어서 배열에 사용하면 객체에 사용하는것보다 10 ~ 100 배 느리다. 물론 이 반복문의 속도가 대체로 빠른편이기에 병목 지점에서만 문제가 되기는 하지만 for...in반복문을 사용할때의 이러한 차이를 잘 알아두고 배열에는 되도록 사용하지 않을 것을 권고하고있다.

length프로퍼티

배열에 무언가 조작을 가하면 length프로퍼티가 자동으로 갱신된다.
이 프로퍼티는 배열 내 요소의 개수가 아니라 가장 큰 인덱스에 1을 더한 값이다
따라서 배열에 요소가 하나있고, 이 요소의 인덱스가 아주 큰 정수라면 배열의 length프로퍼티도 커진다.

let fruits = [];
fruits[123] = "사과"; // 권장하지는 않음..

alert( fruits.length ); // 124

배열에 123번째 인덱스에 요소를 추가했다.
length프로퍼티는 가장 큰 인덱스에 1을 더한 값이다. 여기서는 빈배열에 123인덱스에 요소가 들어있기에 가장 큰 인덱스는 123이 되고 여기 1을 더한 값인 124 가 배열의 길이가 된다.

length프로퍼티의 또 다른 특징중 하나는 쓰기가 가능하다는 것이다.

length의 값을 수동으로 증가시킬때에는 아무런 일도 일어나지 않으나 감소시키면 배열이 잘린다.
일부가 보이는 것이 아니라 진짜 잘리는것이다.

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

arr.length = 2; // 요소 2개만 남기고 잘라봅시다.
alert( arr ); // [1, 2]

arr.length = 5; // 본래 길이로 되돌려 봅시다.
alert( arr[3] ); // undefined: 삭제된 기존 요소들이 복구되지 않습니다.

이러한 특징을 사용하면 arr.length = 0; 을 사용해 간단하게 배열을 비울 수 있다.!!

new Array()

배열을 만들때 사용할 수 있는 방법 중 하나라고 앞에서 언급했었다.
대괄호를 사용하면 더 짧은 문법으로 배열을 만들 수 있기에 잘 사용되지는 않는 문법이다.

숫자형 인수를 하나 넣어 new Arrayf를 호출하면 배열이 만들어지는데 이 배열에는 요소가 없으나 길이는 넣어준 인수와 같아진다.

let arr = new Array(2); // 이렇게 하면 배열 [2]가 만들어질까요?

alert( arr[0] ); // undefined가 출력됩니다. 요소가 하나도 없는 배열이 만들어졌네요.

alert( arr.length ); // 길이는 2입니다.

이러한 뜻밖의 상황을 마주치지 않기 위해 new Array의 기능을 잘 알지 않는 한 대부분은 대괄호를 사용하여 배열을 만든다.

다차원 배열

배열역시 배열의 요소가 될 수 있다.
이러한 배열을 다차원 배열이라고 부른다
다차원 배열은 행렬을 저장하는 용도로 사용된다.

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

alert( matrix[1][1] ); // 5, 중심에 있는 요소

toString

배열에는 toString메서드가 구현되어 있어 이를 호출하게되면 요소를 쉼표로 구분한 문자열이 반환된다.

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true

배열에는 Symbol.toPrimitive 나 valueOf메서드가 없다.
따라서 alert( [] + 1) => "1"
여기에서는 문자열로의 형변환이 일어나 []는 빈문자열, [1] 은 문자열 "1" , [1,2]는 문자열 "1,2" 로 변환된다.

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

이항 덧셈 연산자 "+" 는 피연산자 중 하나가 문자열인 경우 나머지 피연산자도 문자열로 변환한다. 따라서 위의 예시는 문자열과 숫자를 연산하는 결과와 같다고 봐도 무방하다.

연산해보기

  1. 요소 “Jazz”, "Blues"가 있는 styles 배열을 생성합니다.
  2. "Rock-n-Roll"을 배열 끝에 추가합니다.
  3. 배열 정 중앙에 있는 요소를 "Classics"로 바꿉니다. 가운데 요소를 찾는 코드는 요소가 홀수 개인 배열에서도 잘 작동해야 합니다.
  4. 배열의 첫 번째 요소를 꺼내서 출력합니다.
  5. "Rap"과 "Reggae"를 배열의 앞에 추가합니다.(Rap, Reggae, Classics, Rock-n-Roll)
  1. const styles = ["Jazz","Blues"];
  2. styles.push("Rock-n-Roll")
  3. styles[Math.floor((styles.length -1)/2)] = "Classics"
  4. styles.shift()
  5. styles.unshift("Rap","Reggae")

0개의 댓글