FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

JavaScript之typed arrays那些事儿 #164

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

主要参考这篇文章:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays

FrankKai commented 4 years ago

typed arrays基本介绍

FrankKai commented 4 years ago

Buffers和views:typed array 架构

ArrayBuffer

Typed array views

扩展: 1.什么是Web IDL type interface definition language,用来描述打算在web浏览器中实现的interface。

DataView

new DataView(buffer).setInt16(0, 256, true)怎么理解? true是littleEndian,也就是小端模式,低位数据存储在低地址,Int16Array uses the platform's endianness。

扩展: 1.什么是Endianness 内存存储的大小端模式。 所谓大端模式,指的是低位数据高地址,0x12345678,12存buf[0],78(低位数据)存buf[3](高地址)。也就是常规的正序存储。 小端模式与大端模式相反。0x12345678,78存在buf[0],存在低地址。

FrankKai commented 4 years ago

typed arrays的Web API

使用了typed array的web api有以下这些。

拓展:interface mixin Body{}怎么理解? 引出一个很有趣的东西。interCaps。

Rule: Use interCaps for naming. All methods and attributes should begin with a lowercase letter. Every subsequent word in the method or attribute name should be capitalized. Avoid capitalizing first letters and using underscores to separate words. Why: IDL usually follows Java and JavaScript convention, which is intercaps. In addition, the first letter of all names are promoted to upper case in C++. The C++ signature is the same whether or not the first letter is capitalized.

IDL通常用js或者java实现。此处的interface mixin是java实现。

如何将 typedArray 转换为普通数组

处理完typed array以后,很多情况下会将其转换为普通数组。 可以使用Array.from做转换。或者使用以下代码转换。

let typedArray = new Uint8Array([1, 2, 3, 4]);
let normalArray = Array.from(typedArray);
let typedArray = new Uint8Array([1, 2, 3, 4]),
    normalArray = Array.prototype.slice.call(typedArray);
normalArray.length === 4;
normalArray.constructor === Array;
FrankKai commented 4 years ago

typed array使用例子

使用带buffer(缓冲区)的view(视图)

// 创建一个16字节定长的buffer
let buffer = new ArrayBuffer(16);

image 此处会用到通信原理中学到的:1byte = 8bit。(1个字节等于8个比特) 上图中的Int8,Int16,Int32,Uint8中的数字都是以bit为单位的。 其总空间为16*8 = 128bit。 因此Int8Array和Uint8Array类型的ArrayBuffer长度为16。 因此Int16Array类型的ArrayBuffer长度为8。 因此Int32Array类型的ArrayBuffer长度为4。 位数越大,说明ArrayBuffer中的一个0占用的内存空间越大。

// 此时对于一块初始字节数是0的内存,在这块内存上可以为所欲为
// 通过byteLength属性,可以获得一个buffer占用的字节内存空间
console.log(buffer.byteLength); // 16(bytes)
// 此时buffer是无法作用的,我们需要创建一个view
// 以下代码会创建一个将buffer中的数据转换为32-bit的有符号整型数组
let int32View = new Int32Array(buffer);

这里除了可以创建Int32Array的ArrayBufferView,还可以创建Int8Array,Uint8Array,Int16Array等等类型的ArrayBufferView。

// 可以像普通数组一样去遍历ArrayBufferView数组
int32View.forEach((e,i,arr)=>{ arr[i] = i; })// 0,1,2,3

这个数组有4个entry,每个entry占用4个byte(字节),所以总计16个字节。

处理复杂的数据结构

struct someStruct {
  unsigned long id; // long 32bit
  char username[16];// char 8bit
  float amountDue;// float 32bit
};

上面的C结构在js中可以这样定义:

let buffer = new ArrayBuffer(24);
// ... read the data into the buffer ...
let idView = new Uint32Array(buffer, 0, 1);
let usernameView = new Uint8Array(buffer, 4, 16);
let amountDueView = new Float32Array(buffer, 20, 1);

偏移量为什么是1,4,20? 因为32/8 = 4。0到3属于idView。 8/8 =1。4到19属于usernameView。 32/8 = 4。20到23属于amountView。

需要注意C语言的数据结构是与平台对齐的。因此C语言中的long,char,float具体占用多少bit,与平台有关。

扩展

1.Uint32Array(buffer, 0, 1)其中的0,1代表什么? buffer:ArrayBuffer constructor。构造时固定。只读。 byteOffset:0代表从ArrayBuffer开始start位置的offset。构造时固定。只读。 length:ArrayBuffer中元素的个数。构造时固定。只读。 byteLength: ArrayBuffer占用空间的大小。构造时固定。只读。 2.length与byteLength区别是什么?

var buffer = new ArrayBuffer(16);
var int32View = new Int32Array(buffer);
int32View.length; // 4
int32View.byteLength; // 16