11장 새로운 배열 함수, 타입이 있는 배열

2023.06.16

11.1 새로운 배열 메서드

11.1.1 Array.of

  • Array.of는 다음과 같이 이산 인수로 전달한 값을 포함하는 배열을 만들고 반환한다.
1const a = Array.of('one', 'two', 'three');
2console.log(a); // ["one", "two", "three"]
  • Array.of는 리터럴 양식이 없기 때문에 배열 서브클래스에 유용하다.
1class MyArray extends Array {
2  niftyMethod() {
3    // ...do something nifty...
4  }
5}
6const a = MyArray.of('one', 'two', 'three');
7console.log(a instanceof MyArray); // true
8console.log(a); // ["one", "two", "three"]

11.1.2 Array.from

  • Array.from은 이터러블이나 배열과 유사한 객체를 첫 번째 인수로 받아들이고 해당 객체의 값을 사용하여 배열을 만들고 선택적으로 매핑 함수를 적용한다.
1const a = Array.from('123');
2console.log(a); // ["1", "2", "3"]
3
4const b = Array.from({ length: 2, 0: 'one', 1: 'two' });
5console.log(b); // ["one", "two"]
6
7const c = Array.from('0123456789', Number);
8console.log(c); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 매핑 함수는 Array.prototype.map 콜백과 유사하지만, 두 가지 차이가 있다.
    • Array.from 매핑 콜백은 세 번째 인수인 매핑되는 소스 객체를 받지 않는다.
    • map은 소스 배열에 있는 엔트리에 대해서만 콜백을 호출하지만, 배열과 유사한 객체를 처리할 때 from은 0에서 length -1 범위의 모든 인덱스에 대해 콜백을 호출한다. 엔트리가 없는 경우에도 마찬가지이다.
  • 아래와 같이 범위 배열을 만들때 유용하게 사용할 수 있다.
1const a = Array.from({ length: 100 }, (_, index) => index);
2// Or: const a = Array.from(Array(100), (_, index) => index);
3console.log(a); // [0, 1, 2, 3, ... 99]

11.1.3 Array.prototype.keys

  • keys 메서드는 배열의 키에 대한 이터레이터를 반환한다.
  • 배열의 키는 0부터 length - 1까지의 숫자이다.
  • keys 메서드는 다음과 같은 특징이 있다.
    • 배열이 아닌 이터레이터를 반환한다.
    • keys 메서드의 이터레이터가 반환하는 값은 숫자이다.
    • 배열이 희소하더라도 0 ≤ n < length 범위의 모든 인덱스 값은 이터레이터에 의해 반환된다.
    • 배열에 배열 인덱스가 있는 경우 배열 이덱스가 아닌 열거 가능한 속성의 이름을 포함되지 않는다.
1for (const index of ['one', 'two', 'three'].keys()) {
2  console.log(index);
3}
4// =>
5// 0
6// 1
7// 2
8
9const a = [, 'x', , , 'y'];
10for (const index of a.keys()) {
11  console.log(index, index in a ? 'present' : 'absent');
12}
13
14// 배열 a는 인덱스 0, 2, 3에 엔트리가 없다.
15// =>
16// 0 "absent"
17// 1 "present"
18// 2 "absent"
19// 3 "absent"
20// 4 "present"

11.1.4 Array.prototype.values

  • values 메서드는 배열의 값에 대한 이터레이터를 반환한다.
1for (const index of ['one', 'two', 'three'].values()) {
2  console.log(index);
3}
4// =>
5// "one"
6// "two"
7// "three"
8
9const a = [, 'x', , , 'y'];
10for (const value of a.values()) {
11  console.log(value);
12}
13// =>
14// undefined
15// "x"
16// undefined
17// undefined
18// "y"

11.1.5 Array.prototype.entries

  • entries 메서드는 효과적으로 keys와 values 메서드를 조합한다.
  • 배열의 엔트리에 대한 이터레이터를 반환하고 각 엔트리는 [index, value] 배열이다.
  • entries 메서드의 [index, value] 배열은 희소 엔트리의 경우에도 항상 인덱스와 값을 모두 갖는다.
1for (const entry of ["one", "two", "three"].entries()) {
2  console.log(entry);
3}
4// =>
5// [0, "one"]
6// [1, "two"]
7// [2, "three"]
8
9const a = [, undefined, , , "y"];
10for (const [index, value] of a.entries()) {
11  console.log(index, value, index in a ? "present" : "absent");
12}
13// =>
14// 0 undefined "absent"
15// 1 undefined "present"
16// 2 undefined "absent"
17// 3 undefined "absent"
18// 4 "y" "present"

11.1.6 Array.prototype.copyWithin

  • copyWithin 메서드는 배열의 한 부분에서 배열의 다른 부분으로 엔트리를 복사하여 배열의 길이를 늘리지 않고 잠재적인 겸침 문제를 처리한다.
  • 인수 중 하나라도 음수이면 배열 끝에서 오프셋으로 사용된다.
  • 종료 인덱스(세 번째 인수)가 없으면 기본적으로 배열의 길이로 설정된다.
1const a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'];
2console.log('before', a);
3a.copyWithin(2, 8);
4console.log('after ', a);
5// =>
6// before ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]
7// after  ["a", "b", "i", "j", "k", "f", "g", "h", "i", "j", "k"]
8// 인덱스 2~4에 있는 앞쪽 엔트리를 복사본으로 덮어썼다.
9
10const b = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
11console.log('before', b);
12b.copyWithin(4, 2);
13console.log('after ', b);
14// =>
15// before ["a", "b", "c", "d", "e", "f", "g"]
16// after  ["a", "b", "c", "d", "c", "d", "e"]
17// 배열을 확장하지 않고, 복사가 중지되었다. 코드에 종료 인덱스(세 번째 인수가) 없었으므로

11.1.7 Array.prototype.find

  • find 메서드는 조건 함수를 사용하여 배열에서 첫 번째로 일치하는 엔트리의 값을 찾는 것이다.
  • 방문할 엔트리의 범위는 find가 루프를 시작하기 전에 결정된다.
    • 맨 뒤에 새 엔트리를 추가하면 방문하지 않는다.
    • 이미 방문한 엔트리를 변경하면 다시 방문하지 않는다.
    • 아직 방문하지 않은 엔트리를 변경하면 방문할 때 새 값이 사용된다.
    • 배열에서 엔트리를 제거하고 길이를 줄이면 배열의 공백에 대한 일반적인 값인 undefined로 끝에 있는 공백이 방문된다.
1const firstEven = [1, 2, 3, 4, 5, 6].find((value) => value % 2 == 0);
2console.log(firstEven); // 2
3
4const a = ['one', 'two', 'three'];
5const x = a.find((value, index) => {
6  console.log(`Visiting index ${index}: ${value}`);
7  if (index === 0) {
8    a[2] = a[2].toUpperCase();
9  } else if (index === 1) {
10    a.push('four');
11  }
12  return value === 'four';
13});
14console.log(x);
15// =>
16// Visiting index 0: one
17// Visiting index 1: two
18// Visiting index 2: THREE
19// undefined

11.1.8 Array.prototype.findIndex

  • findIndex는 조건 함수가 참으로 평가되는 값을 반환한 엔트리의 인덱스를 반환한다.
  • 엔트리가 부족한 경우 -1을 반환한다.
1const a = [1, 2, 3, 4, 5, 6];
2const firstEven = a.findIndex((value) => value % 2 == 0);
3console.log(firstEven); // 1 -- the first even value is the number 2 at index 1

11.1.9 Array.prototype.fill

  • fill 메서드는 주어진 value를 사용하여 호출한 배열을 채우고 선택적으로 start 및 end 인덱스로 정의된 범위만 채운다.
1console.log(Array(5).fill(42)); // [42, 42, 42, 42, 42]
2
3const a = Array(2).fill({});
4a[0].name = 'Joe';
5a[1].name = 'Bob';
6console.log(a[0].name); // "Bob"
  • Array(2).fill({})은 동일한 객체를 배열의 두 엔트리에 모두 넣지만 배열을 여러 개의 고유한 객체로 채우지 않는다.

11.1.10 Array.prototype.includes

  • ES2016에 추가된 includes 메서드는 사양에 정의된 등가0(SameValueZero) 알고리즘에 따라 주어진 값이 배열이 있으면 true를 반환하고 그렇지 않으면 false를 반환한다.
  • 선택적으로 제공된 start 인덱스가 있으면 해당 인덱스에 검색을 시작한다.
  • start가 음수면 배열 끝에서 오프셋으로 사용된다.
1const a = ['one', 'two', 'three'];
2console.log(a.includes('two')); // true
3console.log(a.includes('four')); // false
4console.log(a.includes('one', 2)); // false, "one" is before index 2
5
6const b = [NaN];
7console.log(b.indexOf(NaN) !== -1); // false
8console.log(b.includes(NaN)); // true
  • 등가0은 NaN을 처리하는 방식에서 엄격한 동등성과 다르니 주의하자.
    • NaN 외에 등가0은 엄격한 동등성과 같다.

11.1.11 Array.prototype.flat

  • ES2019에 추가된 flat은 원래 배열에서 가 값을 가져와서 새로운 평평한 배열을 만들고 값이 배열인 경우 배열 자체가 아닌 결과에 넣을 값을 가져온다.
  • 선태적으로 깊이 인수를 받아서 주어진 깊이까지 재귀적 평탄화를 할 수 있다. ( 기본 값은 1이다. )
1const original = [[1, 2, 3], 4, 5, [6, 7, 8]];
2const flattened = original.flat();
3console.log(flattened);
4// => [1, 2, 3, 4, 5, 6, 7, 8]
5
6const original = [
7  [1, 2, 3],
8  [
9    [4, 5, 6],
10    [7, 8, 9],
11  ],
12];
13const flattened = original.flat();
14console.log(flattened);
15// => [1, 2, 3, [4, 5, 6], [7, 8, 9]];
  • Infinity를 사용하여 깊이에 관계없이 구조를 완전히 평평하게 할 수도 있다.
1const original = ['a', ['b', 'c', ['d', 'e', ['f', 'g', ['h', 'i']]]], 'j'];
2const flattened = original.flat(Infinity);
3console.log(flattened);
4// => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]

11.1.12 Array.prototype.flatMap

  • ES2019에 추가된 flatMap은 결과를 평면화하기 전에 매핑 함수를 통해 각 값을 전달하고 단일 레벨만 평면화한다는 점을 제외하고는 flat과 같다.
1const flattened = [1, 2, 3, 4].flatMap((e) => e === 3 ? ["3a", "3b", "3c"] : e);
2console.log(flattened);
3// => [1, 2, "3a", "3b", "3c", 4]
4
5const original = [1, 2, 3, 4];
6const flattened = original.map((e) => (e === 3 ? ["3a", "3b", "3c"] : e)).flat();
7console.log(flattened);
8// => [1, 2, "3a", "3b", "3c", 4]

11.2 반복, 스프레드, 디스트럭처링

  • ES2015의 배열에는 아래와 같은 특징이 있다.
    • 배열이 이터러블이 되었다.
    • 배열 리터럴은 스프레드 표기법을 포함할 수 있다.
    • 배열은 디스트럭처링할 수 있다.

11.3 배열 순서 유지 정렬

  • ES2019까지 Array.prototype.sort 메서드에서 사용하는 정렬 알고리즘은 “반드시 안정적일 필요는 없음”으로 정의되었다.
  • 두 엔트리가 동일한 것으로 간주되더라도 결과 배열에서 상대적 위치가 여전히 반전될 수 있음을 의미한다.
  • ES2019부터 안정적인 정렬(일반과 타입이 있는 배열 모두)을 구현하려면 sort가 필요하다.
1// (This example should probably use `toLocaleLowerCase` rather than `toLowerCase`, but
2// it doesn't matter for the *specific* data used below. Still...)
3const a = ["b", "B", "a", "A", "c", "C"];
4a.sort((left, right) => left.toLowerCase().localeCompare(right.toLowerCase()));
5console.log(a);
6
7// 위 코드는 여러 가지의 가능한 결과를 생성할 수 있었다.
8// ['a', 'A', 'b', 'B', 'c', 'C']
9// ['A', 'a', 'b', 'B', 'c', 'C']
10// ['A', 'a', 'B', 'b', 'c', 'C']
11// ['A', 'a', 'B', 'b', 'C', 'c']
12// ['a', 'A', 'b', 'B', 'c', 'C']
13// ...
  • ES2019부터 구현은 첫 번째 결과만 일관되게 생성해야 한다.

11.4 타입이 있는 배열

11.4.1 개요

  • 자바스크립트의 전통적인 "배열"은 고정 크기 단위로 분할된 연속적인 메모리 블록이라는 일반적인 컴퓨터 과학 정의의 배열이 아닌 것으로 유명하다.
  • 대신 자바스크립트의 기존 배열은 “배열 인덱스”, 특별한 length 속성, 대괄호를 사용하는 리터럴 표기법. Array.prototype에서 상속된 메서드에 대한 사양의 정의에 맞는 속성 이름에 대한 특수 처리가 있는 다른 모든 객체와 마찬가지로 객체일 뿐이다.
  • 즉, 자바스크립트 엔진은 최적화가 사양과 충돌하지 않는 곳에서 자유롭게 최적화할 수 있다.
  • 자바스크립트의 기존 배열은 강력하고 유용하지만, 때로는 실제 배열이 필요하다.
    • 특히 파일 읽기/쓰기, 그래픽 또는 수학 API와 상호 운용할 때
  • 타입이 있는 배열은 다음을 제외하고 기존 자바스크립트 배열과 같다.
    • 입력 값은 항상 프리미티브 숫자 값(8비트 정수, 32비트 부동 소수점 등)이다.
    • 타입이 있는 배열의 모든 값은 동일한 타입이다(배열 유형에 따라 다름: Uint8Array. Float32Array 등).
    • 배열을 구성하면 길이를 변경할 수 없다.
    • 해당 값은 지정된 바이너리 양식으로 연속 메모리 버퍼에 저장된다.
    • 타입이 있는 배열은 희소할 수 없지만(중간에 공백이 있을 수 없음) 기존 배열은 희소할 수 있다.
    • 타입이 있는 배열은 타입이 다른 경우에도 다른 타입의 배열과 메모리(기본 데이터 버퍼)를 공유할 수 있다.
    • 타입이 있는 배열의 데이터 버퍼는 스레드 간에 전송되거나 공유될 수도 있다(예: 브라우저 의 웹 워커나 노드제이에스의 워커 스레드).
    • 타입이 있는 배열 엔트리의 값을 가져오거나 설정할 때 항상 어떤 형태의 변환이 수반된다 (자바스크립트의 숫자는 Float64이므로 숫자를 Float64Array와 함께 사용하는 경우 제외).
  • 11가지 종류의 타입이 있는 배열
1| 이름              | 값 타입    | 엔트리 사이즈   | 변환 작업                                                | 설명                               |
2| ----------------- | ---------- | --------------- | -------------------------------------------------------- | ---------------------------------- |
3| Int8Array         | Int8       | 1               | ToInt8                                                   | 8비트 2의 보수 부호 있는 정수      |
4| Uint8Array        | Uint8      | 1               | ToUint8                                                  | 8비트 부호 없는 정수               |
5| Uint8ClampedArray | Uint8C     | 1               | ToUint8Clamp                                             | 8비트 부호 없는 정수 (클램프 변환) |
6| Int16Array        | Int16      | 2               | Tolnt16                                                  | 16비트 2의 보수 부호 있는 정수     |
7| Uint16Array       | Uint16     | 2               | ToUint16                                                 | 16비트 부호 없는 정수              |
8| Int32Array        | Int32      | 4               | ToInt32                                                  | 32비트 2의 보수 부호 있는 정수     |
9| Uint32Array       | Uint32     | 4               | ToUint32                                                 | 32비트 부호 없는 정수              |
10| Float32Array      | Float32    | 4               | IEEE-754-2008 사양 규칙을 사용하여 숫자가 Float32로 변환 | 32비트 IEEE-754 이진 부동 소수점   |
11| Float64Array      | Float64/ 8 | (필요하지 않음) | "숫자"                                                   | 64비트 IEEE-754 이진 부동 소수점   |
12| BigInt64Array     | Bigint64   | 8               | ToBigInt64                                               | ES2020의 새로운 기능               |
13| BigUint64Array    | BigUint64  | 8               | ToBigUint64                                              | ES2020의 새로운 기능               |
  • 타입이 있는 배열은 포인터가 있는 미가공 데이터 블록이 아니다.
  • 기존 배열과 마찬가지로 객체이 다. 모든
  • 일반적인 객체 작업은 타입이 있는 배열에서 작동한다.
  • 타입이 있는 배열은 프로토타입, 메서드, length 속성을 가져서 기존 자바스크립트 배열처럼 엔트리가 아닌 속성을 넣을 수 있다.

11.4.2 기본 사용법

  • 타입이 있는 배열은 리터럴 양식이 없다. 생성자를 호출하거나 생성자의 of 또는 from 메서드를 사용하여 타입이 있는 배열을 만들 수 있다.
  • new %TypedArray%(): 길이가 0으로 설정된 배열을 생성한다.
  • new %TypedArray%(length): length 엔트리가 있는 배열을 생성한다. 각 엔트리는 처음에 모든 비트 끄기(0)로 설정된다.
  • new %TypedArray%(object): 객체의 이터레이터(있는 경우)를 사용하거나 배열과 유사하게 처리하고 해당 내용을 읽기 위해 길이와 배열 인덱스 속성을 사용하여 지정된 객체에서 값을 복사하여 배열을 만든다.
  • new %TypedArray%(typedArray): 주어진 타입입 있는 배열에서 값을 복사하여 배열을 생성한다. 이것은 타입이 있는 배열의 이터레이터를 거치지 않는다. 효율성을 위해 기본 버퍼와 직접 작동한다(배열의 유형이 동일한 경우 매우 효율적인 메모리 복사가 될 수 있다).
  • new %TypedArray%(buffer[, start[, length]]): 주어진 버퍼를 사용하여 배열을 생성한다.
1const a1 = new Int8Array(3);
2a1[0] = 1;
3a1[1] = "2"; // Note the string
4a1[2] = 3;
5console.log(a1); // Int8Array(3): [1, 2, 3] -- note 2 is a number
6
7// Using `of`:
8const a2 = Int8Array.of(1, 2, "3");
9console.log(a2); // Int8Array(3): [1, 2, 3] -- "3" was converted to 3
10// Using `from` with an array-like object:
11const a3 = Int8Array.from({ length: 3, 0: 1, 1: "2" });
12console.log(a3); // Int8Array(3): [1, 2, 0] -- undefined was converted to 0
13// Using `from` with an array:
14const a4 = Int8Array.from([1, 2, 3]);
15console.log(a4); // Int8Array(3): [1, 2, 3]
  • 타입이 있는 배열 정수 값 변환은 첫 번째 단계에서 모듈로 연산을 사용한다.
1const a = new Uint8Array(1);
2a[0] = -25.4;
3console.log(a[0]); // 231 !?!!
4
5const a = new Int8Array(1);
6a[0] = 25.4;
7console.log(a[0]); // 25
8a[0] = -25.4;
9console.log(a[0]); // -25
10
11const value = -25;
12const max = 256;
13const negative = value < 0;
14const remainder = Math.abs(value) % max;
15const result = negative ? max - remainder : remainder;
16console.log(result); // 231

11.4.3 ArrayBuffer: 타입이 있는 배열이 사용하는 저장소

  • 모든 타입이 있는 배열은 값을 저장하기 위해 ArrayBuffer를 사용한다.
  • ArrayBuffer는 바이트 단위로 지정된 고정 크기의 연결된 연속 데이터 블록이 있는 객체이다.
  • 버퍼의 데이터에 직접 접근할 수 없으며 타입이 있는 배열이나 DateView를 통해서만 접근할 수 있다.
  • 배열의 버퍼 속성을 통해 타입이 있는 배열에 연결된 버퍼에 접근할 수 있다.
  • ArrayBuffer 생성자에 제공하는 크기는 바이트 단위이다.
1const a = new Int32Array(5);
2console.log(a.buffer.byteLength); // 20 (bytes)
3console.log(a.length); // 5 (entries, each taking four bytes)
4
5const buf = new ArrayBuffer(20);
6const b = new Int32Array(buf);
7console.log(buf.byteLength); // 20 (bytes)
8console.log(b.length); // 5 (entries, each taking four bytes)
9
10const buf2 = new ArrayBuffer(18);
11const c = new Int32Array(buf2); // RangeError: byte length of Int32Array
12// should be a multiple of 4
  • 아래와 같은 예를 통해 사용자로부터 받은 파일을 ArrayBuffer로 읽은 뒤 PNG 파일인지 확인할 수 있다.
    • PNG는 특정 8바이트 헤더로 시작한다.
1const PNG_HEADER = Uint8Array.of(
2  0x89,
3  0x50,
4  0x4e,
5  0x47,
6  0x0d,
7  0x0a,
8  0x1a,
9  0x0a
10);
11function isPNG(byteData) {
12  return (
13    byteData.length >= PNG_HEADER.length &&
14    PNG_HEADER.every((b, i) => b === byteData[i])
15  );
16}
17function show(msg) {
18  const p = document.createElement("p");
19  p.appendChild(document.createTextNode(msg));
20  document.body.appendChild(p);
21}
22document
23  .getElementById("file-input")
24  .addEventListener("change", function (event) {
25    const file = this.files[0];
26    if (!file) {
27      return;
28    }
29    const fr = new FileReader();
30    fr.readAsArrayBuffer(file);
31    fr.onload = () => {
32      const byteData = new Uint8Array(fr.result);
33      show(`${file.name} ${isPNG(byteData) ? "is" : "is not"} a PNG file.`);
34    };
35    fr.onerror = (error) => {
36      show(`File read failed: ${error}`);
37    };
38  });

11.4.4 엔디언(바이트 순서)

  • ArrayBufffers는 바이르르 저장한다. 대부분의 타입이 있는 배열에는 여러 바이트의 저장 공간을 차지하는 엔트리가 있다.
  • 바이트는 메모리에서 어떤 순서로 있어야 할까? 상위 바이트가 먼저 와야 할까? 하위 바이트가 먼저 와야 할까? 정답은 없다.
    • 값이 높은 바이트/낮은 바이트 순서로 저장되면 빅 엔디언 순서이다.
    • 값이 낮은 바이트/높은 바이트 순서로 저장되면 리틀 엔디언 순서이다.
  • 컴퓨터의 아키텍처(특히 CPU)는 일반적으로 해당 시스템의 메모리에 있는 값의 엔디언을 결정한다.
    • Intel과 AMD의 메인스트림 CPU에서 사용하는 x86 아키텍처는 리틀 엔디언이다.
  • 타입이 있는 배열은 사용 중인 컴퓨터의 엔디언을 사용한다.

11.4.5 DataView: 버퍼에 대한 미가공 접근

  • DataView 객체는 ArrayBuffer의 데이터에 대한 미가공 접근을 제공하며, 멀티바이트 데이터 읽기 옵션과 함께 타입이 있는 배열(Int8, Uint8, Int16, Uint16 등)이 제공하는 모든 숫자 양식의 데이터를 읽는 방법을 제공한다.
  • PNG는 다양한 다중 바이트 정수 필드가 빅 엔디언 양식으로 정의되어 있지만 자바스크립트 코드가 빅 엔디언 플랫폼에서 실행되고 있다고 100% 가정할 수 없다.
    • PNG의 크기를 얻고 싶다고 가정하면, 빅 엔디언 Uint 32값을 읽어야 한다.
  • 이것이 DataView가 필요한 이유다.
1const PNG_HEADER_1 = 0x89504e47; // Big-endian first Uint32 of PNG header
2const PNG_HEADER_2 = 0x0d0a1a0a; // Big-endian second Uint32 of PNG header
3const TYPE_IHDR = 0x49484452; // Big-endian type of IHDR chunk
4function show(msg) {
5  const p = document.createElement('p');
6  p.appendChild(document.createTextNode(msg));
7  document.body.appendChild(p);
8}
9document.getElementById('file-input').addEventListener('change', function (event) {
10  const file = this.files[0];
11  if (!file) {
12    return;
13  }
14  const fr = new FileReader();
15  fr.readAsArrayBuffer(file);
16  fr.onload = () => {
17    // INCORRECT, Uint32Array uses architecture endianness,
18    // this code will fail on a little-endian architecture
19    if (fr.result.byteLength >= 24) {
20      const data = new Uint32Array(fr.result, 0, 6);
21      if (data[0] === PNG_HEADER_1 && data[1] === PNG_HEADER_2 && data[3].getUint32(12) === TYPE_IHDR) {
22        const width = data[4];
23        const height = data[5];
24        show(`${file.name} is ${width} by ${height} pixels`);
25        return;
26      }
27    }
28    show(`${file.name} is not a PNG file.`);
29  };
30  fr.onerror = (error) => {
31    show(`File read failed: ${error}`);
32  };
33});

11.4.6 배열 간에 ArrayBuffer 공유

  • new %TypedArray%(buffer[, start[, length]]): 주어진 버퍼를 사용하여 배열을 생성한다.

  • 생성자의 매개변수는 아래와 같다.

    • buffer: 사용할 ArrayBuffer
    • start: 버퍼를 사용하기 시작할 버퍼의 시작 부분에서의 오프셋(바이트). 기본값은 0(처음부터 시작)이다.
    • length: 엔트리 기준 새로운 타입이 있는 배열의 길이(바이트 아님). 기본값은 엔트리의 갯수를 ArrayBuffer의 나머지 부분에 맞춘다.
  • ArrayBuffer는 다음 두 가지 방법으로 여러 유형의 배열 간에 공유할 수 있다.

    • 겹침 없음: 버퍼의 고유한 부분만 사용하는 각 배열

      1const buf = new ArrayBuffer(20);
      2const bytes = new Uint8Array(buf, 0, 8);
      3const words = new Uint16Array(buf, 8);
      4console.log(buf.byteLength); // 20(바이트)
      5console.log(bytes.length); // 8(바이트)
      6console.log(words.length); // 6(6개의 2바이트(16비트) 워드 = 12바이트)
    • 겹침 있음: 버퍼의 동일한 부분을 공유하는 배열

      1const buf = new ArrayBuffer(12);
      2const bytes = new Uint8Array(buf);
      3const words = new Uint16Array(buf);
      4console.log(words[0]); // 0
      5bytes[0] = 1;
      6bytes[1] = 1;
      7console.log(bytes[0]); // 1
      8console.log(bytes[1]); // 1
      9console.log(words[0]); // 257
      10
      11// 1을 byte[0]과 byte[1]에 기록함으로써
      12// 단일 words[0] 엔트리를 구성하는 두 바이트 모두에 1을 기록했다.
      13// 하나는 상위 바이트, 하나는 하위 바이트
      14
      15// 둘 다에 값 1을 넣으면 16비트 값 1 * 256 + 1, 즉 257(0x0101)을 얻는다. ( words[0]에서 얻은 값

11.4.7 타입이 있는 배열의 서브클래싱

  • class구문이나 Reflect.construct와 함께 함수 사용을 통해 타입이 있는 배열을 서브클래스로 만들 수 있다.

11.4.8 타입이 있는 배열 메서드

  • 타입이 있는 배열은 길이가 고정되어 있기 때문에 배열의 길이를 변경하는 방법이 없다. ( pop, push, etc…)
  • 타입이 있는 배열은 flat, flatMap, concat도 없다.
  • 타입이 있는 배열에 대한 map.m filter, slice의 구현에는 또 다른 제한이 있다. Symbol.species를 사용하여 map과 slice과 같은 메서드가 타입이 아닌 배열을 생성하도록 할 수 없다.
1class ByteArray extends Uint8Array {
2  static get [Symbol.species]() {
3    return Array;
4  }
5}
6const a = ByteArray.of(3, 2, 1);
7console.log(a.map((v) => v * 2));
8// => TypeError: Method %TypedArray%.prototype.map called on
9// incompatible receiver [object Array]
  • %TypedArray%.prototype.set

    • set은 넘겨준 배열에서 타입이 있는 배열의 여러 값을 설정한다.
    • set을 사용하여 여러 유형의 배열을 결합할 수 있다.
    1// ==== Prep for the snippet:
    2
    3const a1 = Uint8Array.of(1, 2, 3);
    4const a2 = Uint8Array.of(4, 5);
    5const a3 = Uint8Array.of(6, 7, 8, 9);
    6
    7// ==== The snippet:
    8
    9const all = new Uint8Array(a1.length + a2.length + a3.length);
    10all.set(a1);
    11all.set(a2, a1.length);
    12all.set(a3, a1.length + a2.length);
    13
    14// ==== Not in the snippet, but so you can see it in action:
    15
    16console.log(all);
  • %TypedArray%.prototype.subarray

    • subarray는 원래 배열의 버퍼를 공유하는 호출한 배열의 부분을 새로운 타입이 있는 배열로 만든다.
    • 첫 번째 인자와 두 번째 인자는 모두 바이트가 아닌 엔트리다.
    1const wholeArray = Uint8Array.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    2const firstHalf = wholeArray.subarray(0, 5);
    3console.log(wholeArray); // Uint8Array [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
    4console.log(firstHalf); // Uint8Array [ 0, 1, 2, 3, 4 ]
    5
    6firstHalf[0] = 100;
    7console.log(wholeArray); // Uint8Array [ 100, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
    8console.log(firstHalf); // Uint8Array [ 100, 1, 2, 3, 4 ]
    9
    10const secondHalf = wholeArray.subarray(-5);
    11console.log(wholeArray); // Uint8Array [ 100, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
    12console.log(secondHalf); // Uint8Array [ 5, 6, 7, 8, 9 ]
    13
    14secondHalf[1] = 60;
    15console.log(wholeArray); // Uint8Array [ 100, 1, 2, 3, 4, 5, 60, 7, 8, 9 ]
    16console.log(secondHalf); // Uint8Array [ 5, 60, 7, 8, 9 ]

11.5 과거 습관을 새롭게

11.5.1 find와 findIndex를 사용하여 루프 대신 배열 검색(적절한 경우)

  • find 또는 findIndex 사용을 고려하자.

11.5.2 Array.fill을 사용하여 루프 대신 배열 채우기

  • Array.fill 또는 Array.from을 사용하자.

11.5.3 readAsBinaryString 대신 readAsArrayBuffer 사용

  • 타입이 있는 배열을 통해 데이터 작업을 하는 대신 readAsArrayBuffer를 사용하자.