Array 기능 향상
Array는 JavaScript의 기본 객체입니다. 그러나 JavaScript의 다른 부분은 시간이 지남에 따라 진화해 왔지만 Array는 ECMAScript 5가 사용하기 쉽도록 여러 가지 메서드를 도입할 때까지 동일하게 유지되었습니다. ECMAScript 6은 새로운 생성 메서드, 몇 가지 유용하고 편리한 메서드, Typed Array 작성 기능과 같이 많은 기능을 추가하여 Array를 개선합니다.
Array 생성하기
ECMAScript 6 이전에 Array를 생성하는 두 가지 기본 방법인 Array
생성자와 리터럴 구문이있었습니다. 두 방법 모두 Array 항목을 개별적으로 나열 해야하며 그렇지 않으면 상당히 제한적입니다. Array와 같은 객체 (즉, 숫자 인덱스와 length
프로퍼티를 가진 객체)를 Array로 변환하는 옵션도 제한되어 있으며 종종 추가 코드가 필요했습니다. JavaScript Array를 보다 쉽게 만들기 위해 ECMAScript 6은 Array.of()
와 Array.from ()
메서드를 추가했습니다.
Array.of() 메서드
ECMAScript 6이 JavaScript에 새로운 생성 메서드를 추가한 이유중 하나는 개발자가 Array
생성자를 사용하여 Array를 생성하는 것을 방지할 수 있다는 것입니다. new Array()
생성자는 실제로 전달된 인자의 타입과 개수에 따라 다르게 동작합니다.
|
|
Array
생성자가 하나의 숫자 값을 전달 받으면 Array의 length
프로퍼티는 그 값으로 설정됩니다. 숫자가 아닌 단일 값이 전달되면 해당 값은 Array의 유일한 항목이됩니다. 여러 값이 전달되면 (숫자 든 아니든) 해당 값은 Array의 항목이 됩니다. 전달되는 데이터의 유형을 항상 알지 못하기 때문에 이 동작은 혼란스럽고 위험합니다.
ECMAScript 6은 이 문제를 해결하기 위해 Array.of()
를 도입했습니다. Array.of ()
메서드는 Array
생성자와 비슷하게 작동하지만 하나의 숫자 값에 대해서 특별한 경우가 없습니다. Array.of()
메서드는 파라미터의 개수나 타입에 상관없이 항상 파라미터를 포함하는 Array를 생성합니다. 다음은 Array.of()
메서드를 사용하는 예제들입니다.
|
|
Array.of()
메서드로 Array를 생성하려면 Array에 원하는 값을 전달하면 됩니다. 여기서 첫 번째 예제는 두 개의 숫자를 포함하는 Array를 만들고 두 번째 Array는 한 개의 숫자를 포함하고 마지막 Array는 한 개의 문자열을 포함합니다. 이것은 Array 리터럴을 사용하는 것과 비슷하며, 대부분 네이티브 Array Array.of()
대신 Array 리터럴을 사용할 수 있습니다. 그러나 Array
생성자를 함수에 전달해야 한다면 일관된 동작을 보장하기 위해Array.of()
를 대신 전달할 수 있습니다.
|
|
이 코드에서 createArray()
함수는 Array
생성자 함수와 Array에 삽입할 값을 받아 들입니다. Array.of()
를 createArray()
의 첫번째 파라미터로 전달하여 새로운 Array를 만들 수 있습니다. value
가 숫자가 아닐 것이라는 것을 보장할 수 없다면 Array
를 직접 전달하는 것은 위험합니다.
Array.of()
메서드는 반환 값의 타입을 결정하기 위해Symbol.species
프로퍼티 (9 장에서 설명됨)를 사용하지 않습니다. 대신 현재의 생성자 (of()
메서드 내에 있는this
)를 사용하여 반환할 올바른 데이터 타입을 결정합니다.
Array.from() 메서드
Array가 아닌 객체를 실제 Array로 변환하는 것은 JavaScript에서 항상 번거로운 작업이었습니다. 예를 들어, Array와 유사한
arguments
객체를 가지고 있고 Array처럼 사용하고 싶다면 먼저 변환해야합니다. Array와 유사한 객체를 ECMAScript 5의 Array로 변환하려고 다음 예제와 같은 함수를 작성합니다.
|
|
이 방법은 수동으로 result
Array를 만들고 arguments
의 각 항목을 새로운 Array에 복사합니다. 이 방법이 효과가 있지만 비교적 간단한 작업을 수행하기 위해서 상당한 양의 코드가 필요합니다. 결국, 개발자들은 다음과 같이 Array와 유사한 객체에 Array의 Native slice()
메서드를 호출함으로써 코드의 양을 줄일 수 있다는 것을 발견했습니다.
|
|
이 코드는 이전 예제와 기능적으로 동일하며, slice()
에 대한 this
값을 Array와 유사한 객체로 설정하기 때문에 작동합니다. slice()
는 숫자 인덱스와 length
프로퍼티만 있으면 올바르게 작동하기 때문에 Array와 유사한 객체가 잘 작동합니다.
이 기술은 타이핑이 덜 필요하지만 Array.prototype.slice.call(arrayLike)
호출은 분명히 arrayLike
를 Array로 변환하지 않습니다. “다행히 ECMAScript 6은 Array.from()
메서드를 이용하여 객체를 Array로 변환하는 명백하고도 깨끗한 방법을 제공합니다.
Iterable 또는 Array와 유사한 객체가 첫 번째 파라미터로 주어지면 Array.from()
메서드는 Array를 반환합니다. 다음은 간단한 예제입니다.
|
|
Array.from()
호출은 arguments
에 있는 항목을 기반으로 새로운 Array를 만듭니다. 그래서 args
는 arguments
와 같은 위치에 같은 값을 갖는 Array
의 인스턴스입니다.
Array.from()
메서드는this
를 사용하여 반환할 Array 타입을 결정합니다.
매핑 변환 (Mapping Conversion)
한단계 더 나아가서 Array 변환을 원한다면 Array.from()
에 매핑 함수를 두 번째 파라미터로 제공할 수 있습니다. 이 함수는 Array와 유사한 객체의 각 값에 대해 연산을 수행하고 결과를 최종 Array의 적절한 색인에 저장하기 전에 변환합니다.
|
|
여기서 Array.from()
는 매핑 함수로 (value) => value + 1
을 넘겨 주므로 항목을 저장하기 전에 Array의 각 항목에 1을 더합니다. 매핑 함수가 객체 상에 있다면, 매핑 함수를 위한 this
값을 Array.from()
의 세 번째 파라미터에 선택적으로 전달할 수 있습니다.
|
|
이 예제는 변환을 위한 매핑 함수로 helper.add()
를 전달합니다. helper.add()
는 this.diff
프로퍼티를 사용하기 때문에, this
의 값을 지정하는 Array.from()
에 세 번째 파라미터를 제공해야합니다. 세번째 파라미터 덕분에, Array.from()
은 bind()
를 호출하지 않거나 다른 방식으로 this
값을 지정하지 않고 쉽게 데이터를 변환할 수 있습니다.
Iterables에서 사용하기
Array.from()
메서드는 Array와 유사한 객체와 Iterable에서 실행가능합니다. 즉, 메서드는 Symbol.iterator
프로퍼티를 가진 객체를 Array로 변환할 수 있습니다.
|
|
numbers
객체는 Iterable이기 때문에 numbers
를 직접 Array.from()
에 전달하여 그 값을 Array로 변환할 수 있습니다. 매핑 함수는 각 숫자에 1을 더하여 결과 Array에 1, 2, 3 대신 2, 3, 4가 포함됩니다.
객체가 Array와 유사하거나 Iterable한 경우
Array.from()
에서 Iterator를 사용하여 변환할 값을 결정할 수 있습니다.
모든 Array에 대한 새로운 메서드
ECMAScript 5의 추세를 이어 가면서 ECMAScript 6는 Array에 몇가지 새로운 메서드를 추가했습니다. find()
및 findIndex()
메서드는 개발자가 Array 값을 사용하는 것을 돕기위한 것이지만, fill()
및 copyWithin()
은 Typed array에 대한 유스 케이스에서 영감을 얻어 ECMAScript 6에 도입된 숫자만 사용하는 Array 형식입니다.
find() 및 findIndex() 메서드
ECMAScript 5 이전에는 Array를 검색하는 Built-in 메서드가 없었기 때문에 번거로운 작업이었습니다. ECMAScript 5에는 indexOf()
및 lastIndexOf()
메서드가 추가되었고, 개발자가 Array 내부의 특정 값을 검색할 수 있게되었습니다. 하지만 이 두가지 메서드는 크게 개선되었지만 한번에 하나의 값만 검색할 수 있어 상당히 제한적이었습니다. 예를 들어 일련의 숫자에서 첫번째 짝수를 찾으려면 코드를 직접 작성해야합니다. ECMAScript 6에서는 find()
및 findIndex()
메서드를 도입하여 이 문제를 해결했습니다.
find()
와 findIndex()
는 두개의 파라미터를 받습니다(콜백함수와 콜백 함수 내에서 this
에 사용할 선택적 값). 콜백 함수는 Array요소, Array에있는 해당 요소의 인덱스, map()
과 forEach()
와 같이 Array 자신을 전달하는 동일한 파라미터를 가집니다. 주어진 값이 당신이 정의한 어떤 기준과 일치하면 콜백은 true
를 리턴해야합니다. find()
와 findIndex()
는 콜백 함수가 처음으로 true
를 반환할 때 Array 검색을 중지합니다.
두 메서드의 유일한 차이점은 find()
는 값을 반환하고 findIndex()
는 값이 발견된 인덱스를 반환한다는 것입니다. 다음은 그 예를 보여줍니다.
|
|
이 numbers
Array에서 33보다 큰 첫 번째 값을 찾기 위해 find()
와 findIndex()
를 호출합니다. find()
호출은 35를 반환하고findIndex()
는 numbers
Array에서 35의 위치인 2를 반환합니다.
find()
와 findIndex()
는 값이 아닌 조건과 일치하는 Array 요소를 찾는 데 유용합니다. 하지만 값만을 찾고 싶다면 indexOf()
와 lastIndexOf()
가 더좋은 선택입니다.
fill() 메서드
fill()
메서드는 하나 이상의 Array 요소를 특정 값으로 채웁니다. 값이 전달되면, fill()
은 Array의 모든 값을 그 값으로 덮어 씁니다.
|
|
numbers.fill(1)
을 호출하면 numbers
의 모든 값이 1로 변경됩니다. 모든 요소가 아닌 일부 요소만 변경하려는 경우 시작 인덱스와 배타적 종료 인덱스(종료 인덱스는 포함되지 않음.)를 추가할 수 있습니다.
|
|
numbers.fill(1,2)
호출에서 2는 인덱스 2의 요소부터 채우는 것을 시작한다는 것을 나타냅니다. 배타적 종료 인덱스가 세 번째 파라미터로 지정되지 않으므로 numbers.length
가 종료 인덱스로 사용됩니다 numbers.fill(0, 1, 3)
연산은 인덱스 1과 2의 Array요소를 0으로 채웁니다. 두 번째와 세 번째 인자로 fill()
을 호출하면 전체 Array를 덮어 쓰지 않고 한 번에 여러 Array 요소를 채울 수 있습니다.
시작 또는 끝 인덱스가 음수이면 Array의 길이에 해당 값이 추가되어 최종 위치가 결정됩니다. 예를 들어,
array
가fill()
이 호출되는 Array이고, 시작 인덱스가-1
이면 인덱스는array.length - 1
입니다.
copyWithin() 메서드
copyWithin()
메서드는 Array의 여러 요소를 동시에 변경한다는 점에서 fill()
과 유사합니다. 그러나 Array 요소에 할당할 단일 값을 지정하는 대신 copyWithin()
을 사용하면 Array 자체에서 Array 요소 값을 복사할 수 있습니다. 이를 수행하려면 copyWithin()
메서드에 두개의 파라미터, 즉 메서드가 값을 채우기 시작할 인덱스와 복사할 값이 시작되는 인덱스를 전달해야합니다.
예를 들어 Array의 처음 두 요소의 값을 Array의 마지막 두 항목에 복사하려면 다음 코드를 실행합니다.
|
|
이 코드는 인덱스 2에서 시작하는 값을 numbers
에 붙여 넣기 때문에 인덱스 2와 3이 모두 덮어 쓰여집니다. copyWithin()
의 두번째 파라미터로 0
을 전달하면 인덱스 0에서 값을 복사하기 시작하고 복사할 요소가 없어질 때까지 계속 진행됩니다.
기본적으로, copyWithin()
은 항상 Array의 끝까지 값을 복사하지만 선택적인 세번째 파라미터를 제공하면 덮어쓸 요소의 개수를 제한할 수 있습니다. 세번째 파라미터는 값 복사가 중지되는 배타적 종료 인덱스(종료 인덱스는 포함되지 않음.)입니다. 다음은 그 예제입니다.
|
|
이 예제에서는 선택적 종료 인덱스가 1
로 설정되어 있기 때문에 인덱스 0의 값만 복사됩니다. Array의 마지막 요소는 변경되지 않습니다.
fill()
메서드와 마찬가지로copyWithin()
메서드의 파라미터에 음수를 전달하면 Array의 길이가 자동으로 해당 값에 추가되어 인덱스를 결정합니다.
이 시점에서 fill()
과 copyWithin()
메서드의 이용 사례는 여러분에게 명확하지 않을 수도 있습니다. 이 메서드들은 Typed array에서 유래되었고 일관성을 위해 일반 Array에 추가 되었기 때문입니다. 그러나 다음 절에서 배우 겠지만, 숫자의 비트를 조작하기 위해 Typed array를 사용하면, 이 메서드들은 훨씬 더 유용합니다.
Typed Array
Typed array는 숫자 타입 (이름에서 암시하듯이 모든 타입이 아닌)을 처리하도록 설계된 특수 용도의 Array입니다. Typed array의 기원은 OpenGL ES 2.0의 port인 WebGL이며, <canvas>
엘리먼트가 있는 웹 페이지에서 사용하도록 설계되었습니다. Typed array는 JavaScript에서 빠른 비트 연산을 제공하기 위해 만들어졌습니다.
Native JavaScript의 대한 숫자 계산 연산은 WebGL에서 너무 느렸습니다. 왜냐하면 숫자가 64 비트 부동 소수점 포맷으로 저장되고 필요에 따라 32 비트 정수로 변환 되었기 때문입니다. 이 제한 사항을 우회하고 계산 연산에 더 나은 성능을 제공하기 위해 Typed array가 도입되었습니다. 개념은 모든 단일 숫자를 비트 Array처럼 취급할 수 있으므로 JavaScript Array에서 사용할 수있는 익숙한 메서드를 사용할 수 있다는 것입니다.
ECMAScript 6는 JavaScript 엔진 전반에 걸친 더 나은 호환성과 JavaScript Array와의 상호 운용성을 보장하기 위해 Typed array를 언어의 공식적인 부분으로 채택했습니다. ECMAScript 6 버전의 Typed array는 WebGL 버전과 완전히 똑같지는 않고, ECMAScript 6 버전을 WebGL 버전의 확장 버전으로 생각할 수 있습니다.
숫자 데이터 타입(Numeric Data Types)
JavaScript 숫자는 64 비트를 사용하여 숫자의 부동 소수점 표현을 저장하는 IEEE 754 형식으로 저장됩니다. 이 형식은 JavaScript에서 정수 및 부동 소수점을 모두 나타내며 두 형식 간의 변환은 숫자가 바뀌면서 자주 발생합니다. Typed array를 사용하면 아래의 8 가지 숫자 타입을 저장하고 조작할 수 있습니다.
- Signed 8-bit integer (int8)
- Unsigned 8-bit integer (uint8)
- Signed 16-bit integer (int16)
- Unsigned 16-bit integer (uint16)
- Signed 32-bit integer (int32)
- Unsigned 32-bit integer (uint32)
- 32-bit float (float32)
- 64-bit float (float64)
int8에 맞는 숫자를 일반적인 JavaScript Number 타입으로 나타내면 56 비트가 낭비됩니다. 이러한 비트는 추가 int8 값 또는 56 비트 미만을 필요로하는 다른 숫자를 저장하는 데 사용하는 것이 좋습니다. 비트를 보다 효율적으로 사용하는 것은 Typed array의 사용 예중 하나입니다.
Typed array와 관련된 모든 연산 및 객체는 이 8 가지 데이터 타입을 중심으로 배치됩니다. 그러나 이들을 사용하려면 Array Buffer를 만들어 데이터를 저장해야합니다.
이 책에서는 괄호 안에 보여준 약어들로 이 타입들을 언급할 것입니다. 이러한 약어는 실제 JavaScript 코드에는 나타나지 않습니다. 단지
더 긴 설명들에 대한 약어 일뿐입니다.
Array Buffer
모든 Typed array의 기초는 Array Buffer입니다. Array Buffer는 지정된 바이트 수를 포함할 수있는 메모리입니다. Array Buffer를 만드는 것은 C 언어에서 malloc()
을 호출하여 메모리를 할당하는 것과 비슷합니다. 다음과 같이 ArrayBuffer
생성자를 사용하여 Array Buffer를 생성할 수 있습니다.
|
|
Array Buffer가 생성자를 호출할 때 포함해야하는 바이트 수를 전달합니다. let
문장은 10 바이트 길이의 Array Buffer를 생성합니다. Array Buffer가 생성되면, byteLength
프로퍼티를 체크함으로써 바이트 수를 확인할 수 있습니다.
|
|
또한 slice()
메서드를 사용하여 기존 Array Buffer의 일부를 포함하는 새로운 Array Buffer를 생성할 수 있습니다. slice()
메서드는 Array의 slice()
메서드와 같이 작동합니다. 여러분은 시작 인덱스와 끝 인덱스를 파라미터로 넘기고, 원래의 요소로 구성된 새로운 ArrayBuffer
인스턴스를 얻을수 있습니다.
|
|
위 코드에서, buffer2
는 인덱스 4와 5에서 바이트를 추출하여 생성됩니다. 이 메소드의 Array 버전을 호출할 때와 마찬가지로 slice()
의 두 번째 파라미터는 배타적(포함되지 않습니다.)입니다.
이렇게 저장 위치를 만들었지만 데이터를 쓸 수 없으면 별로 도움이되지 않습니다. 데이터를 쓰려면 View를 만들어야합니다.
Array Buffer는 항상 생성될 때 지정된 정확한 바이트 수를 나타냅니다. Array Buffer 내에 포함된 데이터는 변경할 수 있지만 Array Buffer 자체의 크기는 변경할 수 없습니다.
View를 사용하여 Array Buffer 조작하기
Array Buffer는 메모리 위치를 나타내며, View는 해당 메모리를 조작하는 데 사용할 인터페이스입니다. View는 Array Buffer 또는 Array Buffer의 하위 집합에서 작동하며 숫자 데이터 타입중 하나에 데이터를 읽고 씁니다. DataView
타입은 8개의 모든 숫자 데이터 타입에 대해 조작할 수있는 Array Buffer의 일반 View입니다.
DataView
를 사용하려면 먼저 ArrayBuffer
의 인스턴스를 생성하고 그것을 사용하여 새로운 DataView
를 만듭니다. 다음 예제를 살펴보겠습니다.
|
|
이 예제의 view
객체는 buffer
의 10 바이트 모두에 접근할 수 있습니다. 그리고 Buffer의 일부분에만 View를 생성할 수도 있습니다. 바이트의 시작 오프셋과 포함할 바이트 수를 선택적으로 입력가능합니다. 포함할 바이트 수를 입력하지 않으면, DataView
는 기본적으로 시작 오프셋에서 버퍼 끝까지 선택됩니다.
|
|
여기서 view
는 인덱스 5와 6의 바이트에서만 작동합니다. 이 방법을 사용하면 동일한 Array Buffer를 통해 여러 View를 만들 수 있습니다. 이는 전체 응용프로그램에서 필요에 따라 동적이 아닌 단일 메모리 위치를 사용하려는 경우 유용할 수 있습니다.
View 정보 가져 오기
다음과 같은 읽기 전용 속성을 가져 와서 뷰에 대한 정보를 검색할 수 있습니다.
buffer
- View가 연결된 Array Buffer입니다.byteOffset
- 제공된 경우DataView
생성자에 대한 두 번째 파라미터(기본값 : 0).byteLength
- 제공된 경우DataView
생성자에 대한 세 번째 파라미터(기본적으로 버퍼의byteLength
).
이러한 프로퍼티를 사용하면 다음과 같이 View가 작동중인 위치를 정확하게 검사할 수 있습니다.
|
|
이 코드는 전체 Array Buffer에 대한 View인 view1
과 Array Buffer의 작은 섹션에 대해 작동하는 view2
를 생성합니다. 이 View는 모두 동일한 Array Buffer에서 작동하기 때문에 동일한 buffer
프로퍼티를 갖습니다. 그러나 byteOffset
과 byteLength
는 각 View마다 다릅니다. 각 View가 작동하는 Array Buffer 부분을 반영합니다.
물론 메모리에 대한 정보만 읽는 것은 그다지 유용하지 않습니다. 이점을 얻으려면 해당 메모리에 데이터를 쓰고 해당 메모리에서 데이터를 읽을 수 있어야합니다.
데이터 읽고 쓰기
JavaScript의 8가지 숫자 데이터 타입 각각에 대해 DataView
프로토 타입에는 Array Buffer에서 데이터를 쓰는 메서드와 읽는 메서드가 있습니다. 메서드 이름은 모두 “set”또는 “get”로 시작하고 그 뒤에 데이터 타입 약어가 옵니다. 예를 들어 다음은 int8 및 uint8 값에서 작동할 수있는 읽기 및 쓰기 메서드 목록입니다.
getInt8(byteOffset, littleEndian)
-byteOffset
에서 시작하는 int8 읽기setInt8(byteOffset, value, littleEndian)
-byteOffset
에서 시작하는 int8 쓰기getUint8(byteOffset, littleEndian)
-byteOffset
에서 시작하는 uint8 읽기setUint8(byteOffset, value, littleEndian)
-byteOffset
에서 시작하는 uint8 쓰기
“get” 메서드는 두개의 파라미터, 즉 읽을 바이트 오프셋과 값을 리틀 엔디안으로 읽어야하는지 여부를 나타내는 선택적 값을 사용합니다. (리틀 엔디안은 최하위 바이트가 마지막 바이트 대신에 바이트 0에 있음을 의미합니다.) “set” 메서드는 3 개의 파라미터, 즉 쓸 바이트 오프셋, 쓸 값, 그리고 값이 리틀 엔디안 형식으로 저장되어야 하는지를 나타냅니다.
8 비트 값과 함께 사용할 수있는 메서드만 보여 주었지만 16 비트 값과 32 비트 값에서 동일한 메서드를 사용할 수 있습니다. 각 이름의 8
을 16
또는 32
로 바꾸면 됩니다. 모든 정수 메서드와 함께, DataView
는 또한 부동 소수점 수에 대해 다음과 같은 읽기 및 쓰기 메서드를 가지고 있습니다.
getFloat32(byteOffset, littleEndian)
-byteOffset
에서 시작하는 float32 읽기setFloat32(byteOffset, value, littleEndian)
-byteOffset
에서 시작하는 float32 쓰기getFloat64(byteOffset, littleEndian)
-byteOffset
에서 시작하는 float64 읽기setFloat64(byteOffset, value, littleEndian)
-byteOffset
에서 시작하는 float64 쓰기
“set”및 “get” 메서드는 다음 예제를 참고하십시오.
|
|
이 코드는 2바이트 Array Buffer를 사용하여 두개의 int8 값을 저장합니다. 첫번째 값은 오프셋 0에서 설정되고 두번째 값은 오프셋 1에서 각 값이 전체 바이트 (8 비트)에 걸쳐 반영됩니다. 이러한 값은 나중에 getInt8()
메서드로 위치에서 읽을 수 있습니다. 이 예제에서는 int8 값을 사용하지만 8가지 숫자 타입중 하나를 해당 메서드와 함께 사용할 수 있습니다.
View는 데이터가 이전에 저장된 방식에 관계없이 언제든지 어떤 형식으로든 읽고 쓸수 있기 때문에 흥미롭습니다. 예를 들어 두개의 int8 값을 쓰고 int16 메서드로 Buffer를 읽는 다음 예제를 살펴보겠습니다.
|
|
view.getInt16(0)
에 대한 호출은 View의 모든 바이트를 읽고 그 바이트를 숫자 1535로 해석합니다. 왜 이런 일이 발생하는지 이해하려면 예제에서 각 setInt8()
이 Array Buffer에 어떤 작업을 하는지 살펴 보면 됩니다.
|
|
Array Buffer는 모두 0 인 16비트로 시작합니다. setInt8()
으로 첫번째 바이트에 5
를 쓰면 8 비트 표현으로 00000101 입력됩니다. 두번째 바이트에 -1을 쓰면 해당 바이트의 모든 비트가 1로 설정되며, 이는 -1의 2의 보수 표현입니다. 두번째 setInt8()
호출 후에, Array Buffer는 16비트를 포함하고, getInt16()
은 그 비트들을 십진수로 1535인 하나의 16비트 정수로 읽습니다.
DataView
객체는 이런식으로 다른 데이터 타입을 혼합하여 사용하는 경우에 유용합니다. 그러나 하나의 특정 데이터 타입만 사용하는 경우 타입 특화 View가 더 나은 선택일 수 있습니다.
Typed Array는 View입니다.
ECMAScript 6 Typed Array는 실제로 Array Buffer의 타입별 View입니다. 일반 DataView
객체를 사용하여 Array Buffer를 조작하는 대신 특정 데이터 타입을 적용하는 객체를 사용할 수 있습니다. 8 개의 숫자 데이터 타입에 해당하는 8 개의 타입별 View와 uint8
값에 대한 추가 옵션이 있습니다.
아래 표에서는 ECMAScript 6 사양의 타입별 View의 전체 목록을 간략하게 보여줍니다.
생성자 이름 | 크기 (byte) | 설명 | C언어의 동일 타입 |
---|---|---|---|
Int8Array | 1 | 8-bit 부호있는 정수 | signed char |
Uint8Array | 1 | 8-bit 부호없는 정수 | unsigned char |
Uint8ClampedArray | 1 | 8-bit 부호없는 정수 (값 범위가 0~255로 제한됨) | unsigned char |
Int16Array | 2 | 16-bit 부호있는 정수 | short |
Uint16Array | 2 | 16-bit 부호없는 정수 | unsigned short |
Int32Array | 4 | 32-bit 부호있는 정수 | int |
Uint32Array | 4 | 32-bit 부호없는 정수 | int |
Float32Array | 4 | 32-bit IEEE 부동 소수점 | float |
Float64Array | 8 | 64-bit IEEE 부동 소수점 | double |
왼쪽 열은 Typed Array 생성자를 나열하고 다른 열은 Typed Array에 포함할 수있는 데이터를 설명합니다. Uint8ClampedArray
는 Array Buffer의 값이 0보다 작거나 255보다 크지 않으면 Uint8Array
와 같습니다. Uint8ClampedArray
는 0보다 작은 값을 0으로 변환하고(-1은 0이되고) 255보다 큰 값은 255로 변환합니다(300은 255가됩니다).
Typed Array 연산은 특정 타입의 데이터에서만 작동합니다. 예를 들어 Int8Array
의 모든 연산은 int8
값을 사용합니다. Typed Array의 요소 크기는 Array의 타입에 따라 다릅니다. Int8Array
의 요소는 한 바이트 길이이고, Float64Array
요소는 8바이트를 사용합니다. 다행스럽게도 요소는 일반 Array처럼 숫자 인덱스를 사용하여 액세스하므로 DataView
의 “set”및 “get” 메서드에 대한 다소 어색한 호출을 피할 수 있습니다.
요소 크기
Typed Array는 여러 요소로 구성되며 요소 크기는 각 요소가 나타내는 바이트 수입니다. 이 값은 각 생성자 및 각 인스턴스의 BYTES_PER_ELEMENT
프로퍼티에 저장되므로 요소 크기를 쉽게 확인할 수 있습니다.
|
|
타입 특화 View 만들기
Typed Array 생성자는 여러개의 파라미터를 허용하므로 Typed Array를 만드는 데는 몇 가지 방법이 있습니다. 먼저, DataView
에서 사용하는 것과 동일한 파라미터 (Array Buffer, 선택적 바이트 오프셋 및 선택적 바이트 길이)를 전달하여 새로운 Typed Array를 만들 수 있습니다.
|
|
이 코드에서 두 개의 View는 모두 buffer
를 사용하는 두 개의 Int8Array
인스턴스입니다. view1
과 view2
는 DataView
인스턴스 상에 존재하는buffer
, byteOffset
, byteLength
프로퍼티와 같습니다. 하나의 숫자 타입으로만 작업하면 DataView
를 사용할 때 Typed Array를 사용하는 것으로 쉽게 전환할 수 있습니다.
Typed Array를 만드는 두 번째 방법은 숫자 하나를 생성자에 전달하는 것입니다. 이 숫자는 Array에 할당할 요소 개수 (바이트가 아님)를 나타냅니다. 생성자는 Array 요소의 개수를 나타내는 올바른 바이트 수를 가진 새로운 Buffer를 만들고 length
프로퍼티를 사용하여 Array의 요소 개수를 액세스할 수 있습니다.
|
|
ints
Array는 두 요소를 위한 공간을 가지고 생성됩니다. 각 16 비트 정수는 값 당 2바이트를 필요로하므로 Array에는 4 바이트가 할당됩니다. floats
Array는 5개의 원소를 저장하기 위해 만들어지므로 필요한 바이트 수는 20개 (요소 당 4 바이트)입니다. 두 경우 모두 새로운 Buffer가 생성되고 필요할 경우 buffer
프로퍼티를 사용하여 액세스 할 수 있습니다.
Typed Array 생성자에 파라미터가 전달되지 않으면 생성자는
0
이 전달된 것처럼 작동합니다. 이렇게하면 0바이트가 Buffer에 할당되므로 데이터를 저장할 수 없는 Typed Array가 만들어집니다.
Typed Array을 만드는 세 번째 방법은 객체를 파라미터로 생성자에 전달하는 것입니다. 객체는 다음 중 하나일 수 있습니다.
- Typed Array - 각 요소는 입력된 새로운 Array의 새 요소에 복사됩니다. 예를 들어 int8을
Int16Array
생성자에 전달하면 int8 값이 int16 Array에 복사됩니다. 새 Typed Array에는 전달된 Array Buffer와 다른 Array Buffer가 사용됩니다. - Iterable - 객체의 Iterator가 호출되어 입력된 Array에 삽입할 항목을 검색합니다. View 타입에 대해 유효하지 않은 요소가 있으면 생성자에서 오류가 발생합니다.
- Array - Array 요소가 새로운 Typed Array로 복사됩니다. 타입에 대해 유효하지 않은 요소가 있으면 생성자에서 오류가 발생합니다.
- 유사 Array 객체 - Array와 동일하게 작동합니다.
이 경우 각각 소스 객체의 데이터로 새로운 Typed Array가 만들어집니다. 이것은 다음과 같이 일부 값으로 입력된 Array를 초기화하려는 경우에 특히 유용할 수 있습니다.
|
|
이 예제는 Int16Array
를 생성하고 두개의 값을 가지는 Array로 초기화합니다. 그런 다음 Int32Array
가 만들어지고 Int16Array
가 전달됩니다. 값 25와 50은 두 개의 Typed Array가 완전히 별도의 Buffer를 가지므로 ints1
에서 ints2
로 복사됩니다. 동일한 숫자는 두 입력된 Array 모두에 표시되지만 ints2
는 데이터를 나타내는 데 8바이트를 사용하고 ints1
는 4바이트를 사용합니다.
Typed Array와 기존 Array의 유사점
Typed Array와 일반 Array는 여러 가지면에서 비슷합니다. 이 장에서 이미 살펴본 것처럼 많은 경우 Typed Array를 일반 Array 처럼 사용할 수 있습니다. 예를 들어 length
프로퍼티를 사용하여 입력된 Array에 있는 요소의 수를 확인할 수 있으며 숫자 인덱스를 사용하여 입력된 Array의 요소에 직접 액세스할 수 있습니다.
|
|
이 코드에서는 두개의 요소가 있는 새로운 Int16Array
이 만들어집니다. 요소은 숫자 인덱스를 사용하여 읽고 쓰며 작업의 일부로 값이 자동으로 저장되고 int16 값으로 변환됩니다. 유사점은 거기서 끝나지 않습니다.
일반 Array와 달리
length
프로퍼티를 사용하여 입력된 Array의 크기를 변경할 수 없습니다.length
프로퍼티는 쓰기가 가능하지 않으므로 이를 변경하려는 시도는 non-strict 모드에서 무시되고 strict 모드에서 오류가 발생합니다.
공통 메서드
Typed Array에는 일반 Array 메서드와 기능적으로 동일한 많은 메서드가 포함되어 있습니다. Typed Array에서도 다음 Array 메서드를 사용할 수 있습니다.
copyWithin()
entries()
fill()
filter()
find()
findIndex()
forEach()
indexOf()
join()
keys()
lastIndexOf()
map()
reduce()
reduceRight()
reverse()
slice()
some()
sort()
values()
이 메서드들은 Array.prototype
과 유사하지만 정확히 동일하지는 않습니다. Typed Array 메서드는 숫자 타입 안전성에 대한 추가 검사를 수행하며 Array가 반환되면 일반 Array 대신 Typed Array를 반환합니다 (Symbol.species
때문에). 다음은 그 차이를 보여주는 간단한 예입니다.
|
|
이 코드는 map()
메서드를 사용하여 ints
의 값을 기반으로 새로운 Array를 만듭니다. 매핑 함수는 Array의 각 값을 두 배로 늘리고 새로운 Int16Array
를 반환합니다.
동일한 Iterator
Typed Array에는 일반 Array와 동일한 3 개의 Iterator가 있습니다. 그것들은 entries()
, keys()
, values()
메서드입니다. 즉 일반 Array에서와 마찬가지로 Spread 연산자와 for-of
루프를 사용할 수 있습니다.
|
|
이 코드는 Typed Array ints
와 동일한 데이터를 포함하는 새로운 Array intsArray
를 만듭니다. 다른 Iterable과 마찮가지로 Spread 연산자는 Typed Array를 일반 Array로 쉽게 변환합니다.
of()와 from() 메서드
마지막으로, 모든 Typed Array에는 Array.of()
및 Array.from()
메서드와 같은 정적 메서드 of()
및 from()
이 있습니다. 차이점은 Typed Array의 메서드는 일반 Array 대신 Typed Array를 반환한다는 것입니다. 다음은 이러한 메서드를 사용하여 Typed Array를 만드는 몇 가지 예제입니다.
|
|
이 예제의 of()
및 from()
메서드는 각각 Int16Array
및 Float32Array
를 만드는데 사용합니다. 이러한 방법을 통해 Typed Array를 일반 Array처럼 쉽게 만들 수 있습니다.
Typed Array와 기존 Array의 차이점
Typed Array와 일반 Array의 가장 중요한 차이점은 Typed Array는 일반 Array가 아니라는 것입니다. Typed Array는 Array
로부터 상속받지 않으며 Array.isArray()
는 Typed Array를 전달할 때 false
를 반환합니다.
|
|
ints
변수는 Typed Array이므로 Array
의 인스턴스가 아니며 그렇기 때문에 Array로 식별될 수 없습니다. Typed Array와 일반 Array는 비슷하지만 Typed Array가 다르게 동작하는 몇가지 메서드가 있기 때문에 이 구분은 매우 중요합니다.
작동 방법상의 차이점
일반 Array는 상호 작용할 때 확장 및 축소될 수 있지만 Typed Array는 항상 동일한 크기를 유지합니다. 일반 Array 처럼 Typed Array의 존재하지 않는 숫자 인덱스에 값을 할당할 때 그 작업은 무시됩니다.
|
|
이 예제에서 숫자 인덱스 2
에 5
를 할당하더라도 ints
Array는 전혀 증가하지 않습니다. length
는 동일하게 유지되고 값은 버려집니다.
Typed Array에는 유효한 데이터 타입만 사용되는지 확인하는 검사 기능도 있습니다. 유효하지 않은 값은 대신 0이 사용됩니다.
|
|
이 코드는 Int16Array
에 문자열 값 "hi"
를 사용하려고 시도합니다. 물론 문자열은 Typed Array에서 유효하지 않은 데이터 타입이므로 값은 대신 0
으로 삽입됩니다. Array의 length
는 여전히 하나이며, ints[0]
슬롯이 존재하더라도 0
값만 있습니다.
Typed Array의 값을 수정하는 모든 메서드는 동일한 제한을 적용받습니다. 예를 들어 map()
에 전달된 함수가 Typed Array에 대해 유효하지 않은 값을 반환하면 대신에 0
이 사용됩니다.
|
|
문자열 값 "hi"
는 16 비트 정수가 아니므로 결과 Array에서 0
으로 바뀝니다. 이 오류 수정 동작으로 인해 Typed Array에는 항상 잘못된 데이터가 없기 때문에 잘못된 데이터가 있을때 오류를 던지는 것에 대해 걱정할 필요가 없습니다.
누락된 메서드
Typed Array에는 일반 Array와 동일한 메서드가 많이 있지만 Array 메서드중 몇개는 없습니다. Typed Array에서 다음 메서드는 사용할 수 없습니다.
concat()
pop()
push()
shift()
splice()
unshift()
concat()
메서드를 제외하고 이 목록의 메서드는 크기를 변경할 수 있습니다. Typed Array는 크기를 변경할 수 없으므로 Typed Array에서 사용할 수 없습니다. 두 개의 Typed Array를 연결한 결과(특히 서로 다른 데이터 타입을 처리하는 경우)가 불확실할 수 있기 때문에 concat()
메서드를 사용할 수 없습니다. 이것은 처음에 Typed Array를 사용하는 이유에 반대되는 개념입니다.
추가된 메서드
마지막으로 Typed Array는 set()
과 subarray()
메서드와 같이 일반 Array에는 존재하지 않는 두가지 메서드를 가지고 있습니다. 이 두가지 메서드중 set()
은 다른 Array를 기존의 Typed Array로 복사하는 반면 subarray()
는 기존의 Typed Array의 일부를 새로운 Typed Array로 추출합니다.
set()
메서드는 Array(Typed Array 또는 일반 Array)와 데이터를 선택적으로 삽입할 오프셋을 받습니다. 아무 것도 전달하지 않으면 오프셋의 기본값은 0입니다. 유효한 데이터 타입만 사용되도록 보장되며 Array 파라미터의 데이터는 대상 Typed Array에 복사됩니다.
|
|
이 코드는 네 개의 요소가있는 Int16Array
를 만듭니다. set()
을 처음 호출하면 Typed Array의 첫번째와 두번째 요소에 두개의 값이 복사됩니다. set()
에 대한 두번째 호출은 오프셋 2
를 이용하여 호출이 되고, 이것은 세번째 요소에서부터 배치되어야 함을 나타냅니다.
subarray()
메서드는 선택적인 시작 및 종료 인덱스 (slice()
메서드에서와 같이 종료 인덱스는 배타적입니다.)를 받아 들여 새로운 Typed Array를 반환합니다. 그리고, 두 파라미터를 모두 생략하여 Typed Array의 복제본을 만들 수도 있습니다.
|
|
이 예제에서는 3 개의 Typed Array가 원래 ints
에서 만들어집니다. subints1
Typed Array는 동일한 정보를 포함하는 ints
의 복사본입니다. subints2
Typed Array는 인덱스 2에서 시작하는 데이터를 복사하기 때문에 ints
의 마지막 두 요소만(75 및 100) 복사합니다. subints3
Typed Array는 subarray()
에서 시작 인덱스와 종료 인덱스가 모두 입력되어 호출되므로 ints
의 가운데 두 요소만 복사됩니다.
요약
ECMAScript 6는 Array를 보다 유용하게 만드는 ECMAScript 5의 작업을 승계합니다. Array를 만드는 방법에는 Array.of()
및 Array.from()
메서드 두가지가 더 추가되었습니다. Array.from()
메서드는 Iterable 및 유사 Array 객체를 Array로 변환할 수
있습니다. 두 메서드 모두 파생 Array 클래스에 상속되며 Symbol.species
프로퍼티를 사용하여 반환할 값 타입을 결정합니다.(다른 상속된 메서드 또한 Array를 반환할 때 Symbol.species
를 사용합니다.)
Array에는 몇가지 새로운 메서드가 있습니다. fill()
및 copyWithin()
메서드를 사용하면 Array 요소를 현재 위치에서 변경할 수 있습니다. find()
및 findIndex()
메서드는 일부 조건과 일치하는 Array의 첫번째 요소를 찾는데 유용합니다. 전자는 기준에 맞는 첫번째 요소를 반환하고 후자는 요소의 인덱스를 반환합니다.
Typed Array는 기술적으로 Array가 아니며 Array를 상속하지 않지만, Array처럼 보이고 동작합니다. Typed Array는 8 개의 다른 숫자 데이터 타입중 하나를 포함하며 숫자 또는 일련의 숫자의 기본 비트를 나타내는 ArrayBuffer
객체를 기반으로 작성됩니다. Typed Array는 JavaScript 숫자 타입의 경우처럼 타입이 앞뒤로 변환되지 않기 때문에 비트 연산을 보다 효율적으로 수행하는 방법입니다.
이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다.
의역, 오역, 직역이 있을 수 있음을 알려드립니다.
This post is a translation of this original article [https://leanpub.com/understandinges6/read#leanpub-auto-improved-array-capabilities]
참고
- ECMAScript 6 Block Binding
- ECMAScript 6 문자열과 정규 표현식
- ECMAScript 6 함수
- ECMAScript 6 객체의 확장된 기능
- ECMAScript 6 쉬운 데이터 액세스를 위한 Destructuring
- ECMAScript 6 Symbol과 Symbol 프로퍼티
- ECMAScript 6 Set과 Map
- ECMAScript 6 Iterator와 Generator
- ECMAScript 6 JavaScript 클래스 소개
- ECMAScript 6 Array 기능 향상
- ECMAScript 6 Promise와 비동기 프로그램밍
- ECMAScript 6 프록시와 리플렉션 API
- ECMAScript 6 Module로 코드 캡슐화하기
- ECMAScript 6 부록 A. 작은 변경 사항
- ECMAScript 6 부록 B. ECMAScript 7(2016) 이해하기