배열 in JS

동동·2021년 9월 5일
3

배열은 무엇인가요?

  1. 배열은 메모리 상에 요소를 연속하게 배치한 자료구조입니다.
  2. 배열의 특징은 다음과 같습니다.
    • 임의의 위치에 있는 요소에 O(1)으로 접근・수정할 수 있다.
    • 배열의 끝에 O(1)으로 원소를 추가할 수 있다.
    • 배열의 마지막 요소를 O(1)으로 삭제할 수 있다.
    • 임의의 위치에 요소를 추가・삭제는 O(N)이다.

JS의 배열에 대해서 조금 더 자세히 말해주세요

  1. ECMAScript에서는 배열을 배열 인덱스 프로퍼티 키를 특별히 처리하는 객체로 정의하고 있습니다. 즉, 배열을 배열 인덱스와 값을 연결하는 맵(딕셔너리)로 정의하고 있습니다.

  2. 배열의 고유 프로퍼티는 배열 인덱스와 length이며, 이 둘은 일반 객체의 프로퍼티와는 다르게 동작합니다. 배열은 객체이므로, 요소와 length 이외의 프로퍼티도 가질 수 있습니다.

  3. 따라서 JS의 배열은 아래의 특징을 가집니다.

    • 배열의 크기를 동적으로 늘였다 줄일 수 있습니다. 즉, C++과 같은 정적 언어에서의 배열과 달리 요소를 추가하면 길이가 늘어나고, 임의의 인덱스의 요소(예를 들어 length보다 큰 인덱스의 요소) 에도 접근・할당할 수 있습니다.
    • 배열은 연속적이지 않으며 '구멍'이 있을 수 있습니다. 구멍이란 배열 길이보다 작으면서 아무것도 정의되지 않은 인덱스를 나타냅니다. 이런 인덱스를 읽으려고 하면 undefined가 반환됩니다. Array.prototype의 메서드는 구멍을 다루는 방법이 각기 다릅니다.
      ※ 다만, 실제 JS엔진은 구멍이 없는 배열을 내부적으로 최적화해서 연속 형태로 저장합니다.

배열 인덱스

  • 인덱스는 숫자가 아니라 문자열입니다.

  • 0 ≤ i < 2^32 - 1 의 범위에 있는 정수(i) 문자열 키를 의미합니다.

  • 범위를 넘는 인덱스는 일반적인 프로퍼티 키(문자열)로 취급하며, 배열 요소로 표시되지 않고 length 프로퍼티에도 영향이 없습니다.

  • 프로퍼티 이름이 배열 인덱스인 프로퍼티를 요소라고 부릅니다.

length

  • 모든 배열은 configurable: falselength 프로퍼티를 가집니다.
console.log(Object.getOwnPropertyDescriptors([]));

/*
{
  length: {
    value: 0,
    writable: true,
    enumerable: false,
    configurable: false
  }
}
*/
  • length 프로퍼티의 값은 모든 배열 인덱스보다 숫자적으로 큽니다.
  • length 프로퍼티는 배열에 포함된 요소의 개수를 나타내지 않습니다. 새 요소를 삽입할 위치를 나타내는 포인터 역할을 합니다.
  • 배열에 프로퍼티가 변경될 때마다 이 특성을 유지하기 위해 필요에 따라 배열의 프로퍼티가 조정됩니다.
    예를 들어 length가 변경할 경우, length 보다 큰 배열 인덱스를 가지는 요소는 모두 제거됩니다.
    length 보다 큰 배열 인덱스를 가지는 요소를 추가한 경우, length는 특성을 지키기 위해 가장 큰 배열 인덱스 + 1 이 됩니다.
  • length를 0으로 설정하면 빈 배열이 됩니다. 하지만 이 접근법은 각 배열 요소를 명시적으로 삭제하므로 느릴 수 있습니다. 모순처럼 보이지만, 빈 배열을 새로 만드는 편이 더 빠를 때가 많습니다.

배열은 어떻게 생성하나요?

  1. 배열 리터럴([]): 배열 요소를 나열합니다. 요소의 위치는 묵시적 인덱스입니다.

  2. Array 생성자: new연산자는 옵션입니다. new없이 일반적인 함수처럼 호출해도 new를 쓴 결과와 같습니다.

    ※ new 연산자를 이용한 배열 생성은 권장되는 방법이 아닙니다.

    • Array 생성자는 단일 인자로 숫자를 받는 경우에, 숫자만큼의 길이를 가진 배열을 만듭니다. 하지만, 여러 인자가 들어올 경우, 해당 인자를 요소로 하는 배열을 생성합니다. 따라서, Array 생성자로는 단 하나의 숫자 요소로 이루어진 배열을 생성할 수 없습니다.
    • 길이를 지정해 생성한 빈 배열에는 구멍만 있으므로 생성자를 이런 식으로 쓰는 건 옳지 않습니다.
  3. Array.of: Array 생성자의 단점을 보완하기 위해 ES6에 도입되었습니다. 단일 인자로 숫자를 받은 경우에도 해당 인자를 요소로 하는 배열을 생성합니다.

객체가 배열인지는 어떻게 확인하나요?

  1. arr instanceof Array: 배열은 Array 객체의 인스턴스이므로 instanceof 연산자로도 확인을 할 수 있습니다. 다만, 영역(창 또는 프레임)을 넘어온 배열은 프로토타입 객체로 다른 Array를 가르키므로 instanceof 연산자로 확인 할 수 없습니다.
  2. Array.isArray(arr) : instanceof와 달리 영역(창 또는 프레임)을 넘어온 객체도 정확히 처리한다
console.log(window.Array === Array); // true

배열은 Iterable입니다.

  • 전개 연산자, for-of 구문에서 Iterator가 사용됩니다.
  • [Symbol.iterator]의 값은 Array.prototype.values 입니다. Array.prototype.keys, Array.prototype.values, Array.prototype.entries 모두 Iterator를 반환합니다.

Array.from(iterable)

  • Array.from() 메서드를 사용하면 Iterable과 유사 배열 객체를 배열로 변환할 수 있습니다.
  • Array.from()은 Iterable의 Iterator를 사용해서 각 요소를 순회하여 배열로 변환합니다.
  • Array.from(iter, callback, thisArg) : 만들어질 배열의 인덱스에 결과 값을 저장하기 전에 이터러블의 각 값에 특정 연산을 수행하거나 최종 형태로의 변환 작업을 할 수 있습니다.

추후 정리

타입 배열이란?

  1. 특수한 목적을 가진 배열. 숫자 타입과 동작하도록 설계되었다.
  2. 타입 배열은 WebGL로부터 시작되었으며, WebGL은 OpenGL ES 2.0을 포팅하여 웹 페이지에서 <canvas> 엘리먼트와 사용되도록 설계된 버전을 의미한다. 빠른 산술 비트 연산을 제공하기 위한 포팅 과정의 일부로 타입 배열이 만들어졌다.
  3. 자바스크립트 숫자를 이용한 산술 연산은 숫자를 64비트 부동소수점 형식으로 저장하고 필요에 따라 32비트 정수로 변환했기 때문에 WebGL을 위해서는 너무 느렸다. 타입 배열은 이러한 제약을 피하고 더 나은 성능의 산술 연산을 제공하기 위해 도입되었다. 이 개념은 어떤 단일 숫자든지 비트 배열처럼 처리될 수 있도록 하며, 자바스크립트 배열에서 이용할 수 있는 익숙한 메서드를 사용할 수 있다는 의미이다.
  4. ECMAScript 6에서는 자바스크립트 엔진을 통한 더 나은 호환성 그리고 배열과의 상호 운용성을 보장하기 위해 공식적인 언어의 일부로 타입 배열을 받아들였다. ECMAScript 6 버전의 타입 배열은 WebGL 버전과 완전히 같지는 않지만 ECMAScript 6 버전을 다른 접근법보다 WebGL의 진화한 버전이라 할 수 있는 유사점이 충분히 존재한다.

이형 객체란?

  1. 배열은 이형 객체(Exotic Object)이다.
    https://tc39.es/ecma262/#sec-array-exotic-objects
    Oridnary Object가 아닌 것이 Exotic Object이다.
    Oridnary Object는 Table 6에 정의된 internal slot, internal method대로 동작하는 객체이다.
    Array는 [[DefineOwnProperty]]가 다르게 동작한다.
profile
작은 실패, 빠른 피드백, 다시 시도

1개의 댓글

comment-user-thumbnail
2021년 9월 5일

배열 공부하자마자 보니까 너무 좋네요. Exotic Object 에 대해서 덕분에 처음 접했어요 👍

답글 달기