-
基本概念
-
由来
-
1. ArrayBuffer 对象
-
ArrayBuffer.prototype.byteLength
-
ArrayBuffer.prototype.slice()
-
ArrayBuffer.isView()
2. TypedArray(类型化数组) 视图
-
构造函数
-
数组方法
-
字节序
-
BYTES_PER_ELEMENT 属性
-
溢出
-
TypedArray.prototype.buffer
-
TypedArray.prototype.byteLength,TypedArray.prototype.byteOffset
-
TypedArray.prototype.length
-
TypedArray.prototype.set()
-
TypedArray.prototype.subarray()
-
TypedArray.prototype.slice()
-
TypedArray.of()
-
TypedArray.from()
3. 复合视图
4. DataView 视图
二进制数组的应用
-
AJAX
-
Canvas
-
WebSocket
-
Fetch API
-
File API
Web Worker数据共享之SharedArrayBuffer
-
Atomics 对象
参考
JavaScript处理二进制之ArrayBuffer
TypedArray (类型化数组)
或
DataView
对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
这三个对象都是以数组的语法处理二进制数据,所以统称为二进制数组。
注意,二进制数组并不是真正的数组,而是类似数组的对象。
很多浏览器操作的 API,用到了二进制数组操作二进制数据,下面是其中的几个。
Canvas
Fetch API
File API
WebSockets
XMLHttpRequest
现在已纳入
ES6
标准。
字节序理解
。
TypedArray 数组只能处理小端字节序,如果遇到大端字节序将无法正确解析!
为了解决这个问题,JavaScript 引入
DataView
对象,可以设定字节序,下文会详细介绍。
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
const buffer = new ArrayBuffer(4); const v1 = new Uint8Array(buffer); v1[0] = 2; v1[1] = 1; v1[2] = 3; v1[3] = 7;
const uInt16View = new Uint16Array(buffer);
if (uInt16View[0] === 258) { console.log('OK'); }
uInt16View[0] = 255; uInt16View[0] = 0xff05; uInt16View[1] = 0x0210;
|
Web Worker
worker开启的多线程之间的数据交换可以是各种格式,不仅仅是字符串,也可以是二进制数据。这种交换采用的是复制机制,即一个进程将需要分享的数据复制一份,通过
postMessage
方法交给另一个进程。如果数据量比较大,这种通信的效率显然比较低。很容易想到,这时可以留出一块内存区域,由主线程与 Worker 线程共享,两方都可以读写,那么就会大大提高效率,协作起来也会比较简单(不像
postMessage
那么麻烦)。
ES2017 引入
SharedArrayBuffer
,允许 Worker 线程与主线程共享同一块内存。
SharedArrayBuffer
的 API 与
ArrayBuffer
一模一样,唯一的区别是后者无法共享数据。
1 2 3 4 5 6 7 8 9 10
|
const sharedBuffer = new SharedArrayBuffer(1024);
w.postMessage(sharedBuffer);
const sharedArray = new Int32Array(sharedBuffer);
|
上面代码中,
postMessage
方法的参数是
SharedArrayBuffer
对象。
Worker 线程从事件的
data
属性上面取到数据。
1 2 3 4 5 6 7 8 9 10
|
onmessage = function (ev) { const sharedBuffer = ev.data;
const sharedArray = new Int32Array(sharedBuffer);
};
|
共享内存也可以在 Worker 线程创建,发给主线程。
SharedArrayBuffer
与
ArrayBuffer
一样,本身是无法读写的,必须在上面建立视图,然后通过视图读写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000);
const ia = new Int32Array(sab);
const primes = new PrimeGenerator();
for ( let i=0 ; i < ia.length ; i++ ) ia[i] = primes.next();
w.postMessage(ia);
|
Worker 线程收到数据后的处理如下。
1 2 3 4 5 6 7
|
let ia; onmessage = function (ev) { ia = ev.data; console.log(ia.length); console.log(ia[37]); };
|
js-lock-and-condition
这个库。
注意,浏览器的主线程不宜设置休眠,这会导致用户失去响应。而且,主线程实际上会拒绝进入休眠。
(4)运算方法
共享内存上面的某些运算是不能被打断的,即不能在运算过程中,让其他线程改写内存上面的值。Atomics 对象提供了一些运算方法,防止数据被改写。
1
|
Atomics.add(sharedArray, index, value)
|
Atomics.add
用于将
value
相加到
sharedArray[index]
,返回
sharedArray[index]
旧的值。
1
|
Atomics.sub(sharedArray, index, value)
|
Atomics.sub
用于将
value
从
sharedArray[index]
减去,返回
sharedArray[index]
旧的值。
1
|
Atomics.and(sharedArray, index, value)
|
Atomics.and
用于将
value
与
sharedArray[index]
进行位运算
and
,放入
sharedArray[index]
,并返回旧的值。
1
|
Atomics.or(sharedArray, index, value)
|
Atomics.or
用于将
value
与
sharedArray[index]
进行位运算
or
,放入
sharedArray[index]
,并返回旧的值。
1
|
Atomics.xor(sharedArray, index, value)
|
Atomic.xor
用于将
vaule
与
sharedArray[index]
进行位运算
xor
,放入
sharedArray[index]
,并返回旧的值。
(5)其他方法
Atomics
对象还有以下方法。
Atomics.compareExchange(sharedArray, index, oldval, newval)
:如果
sharedArray[index]
等于
oldval
,就写入
newval
,返回
oldval
。
Atomics.isLockFree(size)
:返回一个布尔值,表示
Atomics
对象是否可以处理某个
size
的内存锁定。如果返回
false
,应用程序就需要自己来实现锁定。
Atomics.compareExchange
的一个用途是,从 SharedArrayBuffer 读取一个值,然后对该值进行某个操作,操作结束以后,检查一下 SharedArrayBuffer 里面原来那个值是否发生变化(即被其他线程改写过)。如果没有改写过,就将它写回原来的位置,否则读取新的值,再重头进行一次操作。
ES2015标准入门—阮一峰