定型数组(typed array)是ECMAScript新增的结构,目的是提升向原生库传输数据的效率。
    实际上,JavaScript并没有“TypedArray”类型,它所指的其实是一种特殊的包含数值类型的数组
    6.3.1 历史
    1.WebGL
    在WebGL的早期版本中,因为JavaScript数组与原生数组之间不匹配,所以出现了性能问题。图形驱动程序API通常不需要以JavaScript默认双精度浮点格式传递给它们的数值,而这恰恰是JavaScript数组在内存中的格式。因此,每次WebGL与JavaScript运行时之间传递数组时,WebGL绑定都需要在目标环境分配新数组,以其当前格式迭代数组,然后将数值转型为新数组中的适当格式,而这些要花费很多时间。
    2.定型数组
    这当然是难以接受的,Mozilla为解决这个问题而实现了CanvasFloatArray。这是一个提供JavaScript接口的、C语言风格的浮点值数组。JavaScript运行时使用这个类型可以分配、读取和写入数组。这个数组可以直接传给底层图形驱动程序API,也可以直接从底层获取到。最终,CanvasFloatArray变成了Float32Array,也就是今天定型数组中可用的第一个“类型”。
    6.3.2 ArrayBuffer
    Float32Array实际上是一种“视图”,可以允许JavaScript运行时访问一块名为ArrayBuffer的预分配内存。ArrayBuffer是所有定型数组及视图引用的基本单位。
    ArrayBuffer()是一个普通的JavaScript构造函数,可用于在内存中分配特定数量的字节空间。
    image.png
    ArrayBuffer一经创建就不能再调整大小。不过,可以使用slice()复制其全部或部分到一个新实例中:
    image.png
    不能仅通过对ArrayBuffer的引用就读取或写入其内容。要读取或写入ArrayBuffer,就必须通过视图。视图有不同的类型,但引用的都是ArrayBuffer中存储的二进制数据。
    6.3.3 DataView
    第一种允许你读写ArrayBuffer的视图是DataView。
    这个视图专为文件I/O和网络I/O设计,其API支持对缓冲数据的高度控制,但相比于其他类型的视图性能也差一些。
    DataView对缓冲内容没有任何预设,也不能迭代。必须在对已有的ArrayBuffer读取或写入时才能创建DataView实例。
    这个实例可以使用全部或部分ArrayBuffer,且维护着对该缓冲实例的引用,以及视图在缓冲中开始的位置。
    要通过DataView读取缓冲,还需要几个组件:
    ❑ 首先是要读或写的字节偏移量。可以看成DataView中的某种“地址”。
    ❑ DataView应该使用ElementType来实现JavaScript的Number类型到缓冲内二进制格式的转换。
    ❑ 最后是内存中值的字节序。默认为大端字节序。
    1.ElementType
    DataView对存储在缓冲内的数据类型没有预设。它暴露的API强制开发者在读、写时指定一个ElementType,然后DataView就会忠实地为读、写而完成相应的转换。
    DataView为上表中的每种类型都暴露了get和set方法,这些方法使用byteOffset(字节偏移量)定位要读取或写入值的位置。类型是可以互换使用的
    2.字节序
    “字节序”指的是计算系统维护的一种字节顺序的约定。
    DataView只支持两种约定:大端字节序和小端字节序。
    大端字节序也称为“网络字节序”,意思是最高有效位保存在第一个字节,而最低有效位保存在最后一个字节。
    小端字节序正好相反,即最低有效位保存在第一个字节,最高有效位保存在最后一个字节。
    JavaScript运行时所在系统的原生字节序决定了如何读取或写入字节,但DataView并不遵守这个约定。对一段内存而言,DataView是一个中立接口,它会遵循你指定的字节序。
    DataView的所有API方法都以大端字节序作为默认值,但接收一个可选的布尔值参数,设置为true即可启用小端字节序。
    3.边界情形
    DataView完成读、写操作的前提是必须有充足的缓冲区,否则就会抛出RangeError
    DataView在写入缓冲里会尽最大努力把一个值转换为适当的类型,后备为0。如果无法转换,则抛出错误
    6.3.4 定型数组
    定型数组是另一种形式的ArrayBuffer视图。
    虽然概念上与DataView接近,但定型数组的区别在于:它特定于一种ElementType且遵循系统原生的字节序。
    相应地,定型数组提供了适用面更广的API和更高的性能。
    设计定型数组的目的就是提高与WebGL等原生库交换二进制数据的效率。
    由于定型数组的二进制表示对操作系统而言是一种容易使用的格式,JavaScript引擎可以重度优化算术运算、按位运算和其他对定型数组的常见操作,因此使用它们速度极快。
    创建定型数组的方式包括读取已有的缓冲、使用自有缓冲、填充可迭代结构,以及填充基于任意类型的定型数组。
    另外,通过.from()和.of()也可以创建定型数组
    image.png
    定型数组的构造函数和实例都有一个BYTES_PER_ELEMENT属性,返回该类型数组中每个元素的大小
    如果定型数组没有用任何值初始化,则其关联的缓冲会以0填充
    1.定型数组行为从很多方面看,定型数组与普通数组都很相似。
    定型数组支持如下操作符、方法和属性:
    ❑ []❑ copyWithin()❑ entries()❑ every()❑ fill()❑ filter()❑ find()❑ findIndex()❑ forEach()❑ indexOf()❑ join()❑ keys()❑ lastIndexOf()❑ length❑ map()❑ reduce()❑ reduceRight()❑ reverse()❑ slice()❑ some()❑ sort()❑ toLocaleString()❑ toString()❑ values()
    其中,返回新数组的方法也会返回包含同样元素类型(element type)的新定型数组
    image.png
    2.合并、复制和修改定型数组
    定型数组同样使用数组缓冲来存储数据,而数组缓冲无法调整大小。
    因此,下列方法不适用于定型数组:❑ concat()❑ pop()❑ push()❑ shift()❑ splice()❑ unshift()
    不过,定型数组也提供了两个新方法,可以快速向外或向内复制数据:set()和subarray()。
    set()从提供的数组或定型数组中把值复制到当前定型数组中指定的索引位置
    subarray()执行与set()相反的操作,它会基于从原始定型数组中复制的值返回一个新定型数组。复制值时的开始索引和结束索引是可选的
    定型数组没有原生的拼接能力,但使用定型数组API提供的很多工具可以手动构建
    3.下溢和上溢
    定型数组中值的下溢和上溢不会影响到其他索引,但仍然需要考虑数组的元素应该是什么类型。定型数组对于可以存储的每个索引只接受一个相关位,而不考虑它们对实际数值的影响
    除了8种元素类型,还有一种“夹板”数组类型:Uint8ClampedArray,不允许任何方向溢出。超出最大值255的值会被向下舍入为255,而小于最小值0的值会被向上舍入为0