浅析Uint8Array语法及常见使用、Uint8Array.slice与Uint8Array.subarray区别(是否指向同一个内存空间)、new Uint8Array(typedArray)构造函数对typedArray的引用问题(保持同一个引用)、Uint8Array与String互相转换

摘要:
console.log(uint8[0]);//2控制台日志(uint8.BYTES_PER_ELEMENT);控制台日志(arr[1]);variable=newUint8Array(x);//21//来自ArrayBuffer=newArrayBuffer(8);0]//从迭代器变量=function*(){yield*[1,

一、Uint8Array 介绍

  Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。

  详细介绍见 MDN 描述:https://developer.mozilla.org/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文档,对象的slicesubarray方法字面描述基本一样。于是使用subarray方法取而代之。结果自然是很悲催了,解析出每帧数据都一样(解码过程中有去修改对象数据)。

  到这里大概已经猜到了subarrayslice的区别就在于内存空间占用上。为探个究竟,跑个简单的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;

console.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]

  果然,修改ba对应的值也是随之变化的,说明是在同一内存空间上。而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
}

免责声明:文章转载自《浅析Uint8Array语法及常见使用、Uint8Array.slice与Uint8Array.subarray区别(是否指向同一个内存空间)、new Uint8Array(typedArray)构造函数对typedArray的引用问题(保持同一个引用)、Uint8Array与String互相转换》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ansible 基本命令学习与踩坑django 解决白名单下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#中创建、打开、读取、写入、保存Excel的一般性代码

---转载:http://hi.baidu.com/zhaocbo/item/e840bcf941932d15fe358228 1. Excel对象微软的Excel对象模型包括了128个不同的对象,从矩形,文本框等简单的对象到透视表,图表等复杂的对象.下面我们简单介绍一下其中最重要,也是用得最多的四个对象。(1) Application对象。Applic...

Java设计模式学习记录-状态模式

前言 状态模式是一种行为模式,用于解决系统中复杂的对象状态转换以及各个状态下的封装等问题。状态模式是将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象的状态可以灵活多变。这样在客户端使用时无需关心对象的状态,可以实现自身的一致性处理。最近工作有些忙,更新博客慢了。还是要严格要求自己的,抽时间也要坚持学习。  状态模式 概念介绍 状态模式允许...

golang学习之生成代码文档

go doc 工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释,并生成相关文档。 一般用法: go doc package 获取包的文档注释,例如:go doc fmt 会显示使用 godoc 生成的 fmt 包的文档注释。 go doc package/subpackage 获取子包的文档注释,例如:go doc cont...

12种JS常用获取时间的方式

在编程中,总会遇到各种各样的获取时间的要求,下面我们来看一下获取不同时间格式的方法有哪些?如果不记得的话建议收藏哦! 1、获取当前的日期和时间 方法:new Date() console.log(new Date())//Wed Nov 04 2020 18:20:49 GMT+0800 (中国标准时间) 2、获取当前日期 可运行代码: console.l...

Object非空判断

类Objects,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,对对象为null的值进行了抛出异常操作。 public static T requireNonNull(T obj) :查看指定引用对象不是null。 查看源码发现这里对为null的进行了抛出异常操作:...

springboot配置rabbitmq的序列化反序列化格式

SpringBoot封装了rabbitmq中,发送对象和接收对象时,会统一将对象和消息互相转换 会用到MessageConverter转换接口 在发送消息时, 会将Object转换成Message Message createMessage(Object object, MessageProperties messageProperties) 接收消息...