前言
很多时候我们前端开发是用不到 ArrayBuffer 的,但是用不到 ArrayBuffer 不代表我们不需要了解这个东西。本文就围绕 ArrayBuffer 来讲一下相关知识,大概需要10分钟左右就可以读完本文。
什么是ArrayBuffer?
描述
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer MDN
ArrayBuffer具有如下的特性
1.ArrayBuffer 是固定长度连续内存空间的引用;
2.ArrayBuffer 对象代表存储一段二进制数据的内存;
3.ArrayBuffer 并不能直接读写,只能通过视图(Typed Array View | Data View)来读写;
创建一个Buffer
ArrayBuffer 接收一个 length
参数用于描述要创建的ArrayBuffer的大小,单位是字节。length
的值应该是大于0的整数,但是如果你传入的是一个小数,那么会自动向下取整去创建。
// 创建一个长度为0的buffer
const buffer_0 = new ArrayBuffer(0); // success
// 创建一个长度为8的buffer
const buffer_1 = new ArrayBuffer(8); // success
// 创建一个长度为3的buffer
const buffer_2 = new ArrayBuffer(3); // success
根据传入不同的长度,其视图也是不一样的(后面视图部分会详细介绍不同视图的区别),根据下图可以看到,buffer实例具有byteLength
属性可以获取长度。
判断是否是视图实例
ArrayBuffer有两种视图用于提供读取、写入的能力,一种是Typed Array 类型视图一种是Data View。当我们需要判断一个参数是否是符合条件的视图时,可以用isView这个方法。具体判断如下:
ArrayBuffer.isView(new Uint8Array()); // true
ArrayBuffer.isView(buffer); // false
Typed Array 和 Data View 的区别?
什么是Typed Array ?
Typed Array是一个拥有9种类型的视图集合,每一种都是一个构造函数。
Int8Array
:8位有符号整数,长度1个字节,范围是[-128 ~ 128]
。
Uint8Array
:8位无符号整数,长度1个字节,范围是[0 ~ 255)
。
Uint8ClampedArray
:8位无符号整数,长度1个字节,溢出处理不同。
Int16Array
:16位有符号整数,长度2个字节,范围是[-32768 ~ 32767]
。
Uint16Array
:16位无符号整数,长度2个字节,范围是[0 ~ 65536)
。
Int32Array
:32位有符号整数,长度4个字节。
Uint32Array
:32位无符号整数,长度4个字节。
Float32Array
:32位浮点数,长度4个字节。
Float64Array
:64位浮点数,长度8个字节。
我们从开文知道ArrayBuffer存储的是字节长度,而一个字节是8位,同时根据上面每个视图的长度得知总的位数,因此可以推导出来每一个视图的取值范围具体是多少。具体的操作逻辑如下:
// 创建一个长度为8的buffer
const buffer = new ArrayBuffer(8);
// 创建一个无符号8位视图
const view = new Uint8Array(buffer);
// 赋值
view[0] = 1;
view[1] = 2;
结果如下:
从上面的结果中可以看到,Uint8Array
的结果是咱们赋值时候的具体值,但是 Int16Array
为什么会是513呢?我们接着往下看:
当我们执行 view[0] = 1
时,我们在内存中开辟了一个字节的内存,具体如下图:
当我们执行 view[0] = 2
时,我们在内存中开辟了一个字节的内存,具体如下图:
以上是存放在Uint8Array
时候的,视图中每一个位置存放一个字节的数值。但是如果转换成Int16Array
的时候,一个位置要存放2个字节的数值。所以其中第0个位置和第1个位置的值需要放置在一起,通过下图计算可得上图的513。
什么是Data View ?
相较于TypedArray,DataView对ArrayBuffer的操作更加灵活。因为TypedArray操作ArrayBuffer时每个元素占用的字节大小是固定的,要么8位要么16位或者32位等等。而DataView可以从ArrayBuffer中自由的读写多种数据类型,从而控制字节顺序。
创建一个Data View
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
创建视图后如下,可以看到原型上和(Typed View)类型视图是有区别的。
设置数值
原型上有以下方法用于写入和读取,
getInt8/setInt8
getUint8/setUint8
getInt16/setInt16
getUint16/setUint16
getInt32/setInt32
getUint32/setUint32
getFloat32/setFloat32
getFloat64/setFloat64
赋值代码如下:
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setInt8(0, 1);
view.setInt8(1, 2);
view.setInt8(2, 100);
view.setInt8(5, 100);
效果如下:
ArrayBuffer与Blob的区别
ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区,Blob表示一个不可变、原始数据的类文件对象。
Blob类型只有slice方法,用于返回一个新的Blob对象,包含了原Blob对象中指定范围内的数据。
对比发现,ArrayBuffer的数据,是可以按照字节去操作的,而Blob的只能作为一个对象去处理。所以ArrayBuffer相对于Blob更接近真实的二进制,更底层。
ArrayBuffer与Blob的相互转化
var buffer = new ArrayBuffer(16)
var blob = new Blob([buffer])
var blob = new Blob([1,2,3,4,5])
var reader = new FileReader()
reader.readAsArrayBuffer(blob)
reader.onload = function(e) {// e.target.result 即为ArrayBuffer
}
ArrayBuffer使用场景
1.Ajax
传统上,服务器通过 AJAX 操作只能返回文本数据,即responseType
属性默认为text
。XMLHttpRequest
第二版XHR2
允许服务器返回二进制数据,这时分成两种情况。如果明确知道返回的二进制数据类型,可以把返回类型(responseType
)设为arraybuffer
;如果不知道,就设为blob
。
let xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {let arrayBuffer = xhr.response;// ···
};
xhr.send();
2.Canvas
网页Canvas
元素输出的二进制像素数据,就是 TypedArray 数组。
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const uint8ClampedArray = imageData.data;
需要注意的是,上面代码的uint8ClampedArray
虽然是一个 TypedArray 数组,但是它的视图类型是一种针对Canvas
元素的专有类型Uint8ClampedArray
。这个视图类型的特点,就是专门针对颜色,把每个字节解读为无符号的 8 位整数,即只能取值 0 ~ 255,而且发生运算的时候自动过滤高位溢出。这为图像处理带来了巨大的方便。
3.WebSocket
WebSocket
可以通过ArrayBuffer
,发送或接收二进制数据。
let socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';
// Wait until socket is open
socket.addEventListener('open', function (event) {// Send binary dataconst typedArray = new Uint8Array(4);socket.send(typedArray.buffer);
});
// Receive binary data
socket.addEventListener('message', function (event) {const arrayBuffer = event.data;// ···
});
4.Fetch API
Fetch API 取回的数据,就是ArrayBuffer
对象。
fetch(url)
.then(function(response){return response.arrayBuffer()
})
.then(function(arrayBuffer){// ...
});
5.File API
如果知道一个文件的二进制数据类型,也可以将这个文件读取为ArrayBuffer
对象。
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {const arrayBuffer = reader.result;// ···
};