浅析Uint8Array语法及常见使⽤、Uint8Array.slice与Uint8Arr。
。。
⼀、Uint8Array 介绍
Uint8Array 数组类型表⽰⼀个8位⽆符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的⽅式或使⽤数组下标索引的⽅式引⽤数组中的元素。
详细介绍见 MDN 描述:/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
我们要使⽤了解的话,主要看下⽰例代码:
// 来⾃长度
var uint8 = new Uint8Array(2);
uint8[0] = 42;
console.log(uint8[0]); // 42
console.log(uint8.length); // 2
console.log(uint8.BYTES_PER_ELEMENT); // 1
// 来⾃数组
var arr = new Uint8Array([21,31]);
console.log(arr[1]); // 31
// 来⾃另⼀个 TypedArray
var x = new Uint8Array([21, 31]);
var y = new Uint8Array(x);
console.log(y[0]); // 21
// 来⾃ ArrayBuffer
var buffer = new ArrayBuffer(8);
var z = new Uint8Array(buffer, 1, 4);
z; // Uint8Array(4) [0, 0, 0, 0]
// 来⾃⼀个迭代器
var iterable = function*(){ yield* [1,2,3]; }();
var uint8 = new Uint8Array(iterable);
// Uint8Array[1, 2, 3]
⼆、Uint8Array.slice 与 Uint8Array.subarray 区别
最近在处理 png 图像解码时,使⽤到Uint8Array对象。发现该对象在部分android浏览器中没有slice⽅法。翻了⼀遍API⽂档,对象
的slice与subarray⽅法字⾯描述基本⼀样。于是使⽤subarray⽅法取⽽代之。结果⾃然是很悲催了,解析出每帧数据都⼀样(解码过程中有去修改对象数据)。
到这⾥⼤概已经猜到了subarray与slice的区别就在于内存空间占⽤上。为探个究竟,跑个简单的demo
来验证下:
let a = new Uint8Array([1,2,3,4,5,6]);
let b = a.subarray(3,5);
let c = a.slice(3,5);
// 将b的第⼀个值改为9
b[0] = 9;
字符串截取方法sliceconsole.log('a',a);
// 输出:a Uint8Array(6) [1, 2, 3, 9, 5, 6]
console.log('b',b);
// 输出:b Uint8Array(2) [9, 5]
console.log('c',c);
/
/ 输出:c Uint8Array(2) [4, 5]
果然,修改b,a对应的值也是随之变化的,说明是在同⼀内存空间上。⽽c不与前者内存共享,是在独⽴的空间上。
问题是到了,解决办法就⾃然简单了。⽅法有⽆数种。检查原型是否有对应的⽅法肯定是必不可少的。如果原型上没有slice就⾃⾏往原型上添加⼀个即可。循环效率较低,这⾥还是决定使⽤原型本⾝的subarray来处理。最后解决问题的兼容代码如下:
// 兼容代码,如果原型上⽆`slice`则添加⼀个
if(!Uint8Array.prototype.slice){
Uint8Array.prototype.slice = function(...arg){
return new Uint8Array(this).subarray(...arg);
}
};
let a = new Uint8Array([1,2,3,4,5,6]);
let b = b.slice(3,5);
console.log('a',a);
// 输出:a Uint8Array(6) [1, 2, 3, 4, 5, 6]
console.log('b',b);
// 输出:b Uint8Array(2) [4, 5]
这⾥其实也⽐较简单,就是新建了⼀个 Unit8Array 然后去 subarray()。
三、Uint8Array 构造函数对 typedArray 的引⽤问题
Javascript 的 Uint8Array ⽀持字节数据,对于操作⼆进制数据⾮常有⽤,笔者初次接触时发现它有⼏个构造函数,如下:
new Uint8Array();
new Uint8Array(length);
new Uint8Array(typedArray);
new Uint8Array(object);
new Uint8Array(buffer [, byteOffset [, length]]);
这些函数都返回⼀个 Uint8Array 类型的对象,但对于 new Uint8Array(typedArray); 这个形式的构造函数需要理解⼀下。在MDN的官⽅⽂档⾥描述不详:
new Uint8Array(typedArray) 表⽰根据 typedArray 提供的对象创建⼀个 Uint8Array 对象,并保持对typedArray对象的引⽤,这个形式的构造函数不会复制typedArray对象的。尝试以下代码,看看输出结果。
// create a TypedArray with a size in bytes
var buffer = new ArrayBuffer(8);
var typedArray1 = new Uint8Array(buffer);
typedArray1[0] = 32;
typedArray1[1] = 33;
typedArray1[2] = 34;
var typedArray2 = new Uint8Array(buffer);
typedArray2[0] = 42;
typedArray2[1] = 43;
typedArray2[2] = 44;
console.log(typedArray1);
//不是输出 Uint8Array [32, 33, 34, 0, 0, 0, 0, 0]
//正确输出 Uint8Array [42, 43, 44, 0, 0, 0, 0, 0]
console.log(typedArray2);
//正确输出 Uint8Array [42, 43, 44, 0, 0, 0, 0, 0]
从以上代码看到了 typedArray1 与 typedArray2 输出完全⼀样。原因就是 typedArray1.buffer 与 typedArray2.buffer 指向的是同⼀个对象,因此分别修改 typedArray1 与 typedArray2 时,实际上修改的是同⼀内存对象。
When creating an instance of a TypedArray (e.g. Int8Array), an array buffer is created internally in memory or, if an ArrayBuffer object is given as constructor argument, then this is used instead. The buffer address is saved as an internal property of the
instance and all the methods of %TypedArray%.prototype, i.e. set value and get value etc., operate on that array buffer address.
因此,需要注意的是:⽤ ArrayBuffer 作为构造函数的参数时,Uint8Array直接引⽤这个ArrayBuffer对象作为内部缓冲,⽽不再创建内部ArrayBuffer对象。
四、Uint8Array 与 String 互转
1、字符串转Uint8Array
function stringToUint8Array(str){
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr); return tmpUint8Array
}
2、Uint8Array转字符串
function Uint8ArrayToString(fileData){
var dataString = "";
for (var i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]); }
return dataString
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论