您的位置:首页 > Web前端 > JavaScript

遇见Javascript类型数组

2012-04-02 10:43 309 查看
我在Chrome的最新动态里提到了Typed Arrays(Typed Array,类型数组)这个概念,可能对很多人来说非常陌生,那么它是什么,又有什么用途呢?

之前的问题

Web应用程序变得越来越强大,例如新增了音视频处理、WebSocket等多个功能特性。毫无疑问,如果Javascript能够快速方便的操作原始二进制数据会相当的有用。过去,我们必须要把原始数据当作字符串来处理,并且使用charCodeAt方法来从数据缓冲区中读取字节。

但是这种方法需要多次转换数据(尤其在二进制数据不是字节格式的数据时,例如32位整数或者浮点数),所以非常慢而且容易出错。

Javascript需要一种机制来更有效的访问原始的二进制数据,由此产生了类型数组。

定义

其实除了Javascript,类型数组在其他很多语言中也有。它是一种数组,只有一种变量的类型。例如,一个float类型的数组将只包含浮点数而不能混用字符串和浮点数。此外,一个类型数组在初始化后不能改变大小。它看起来形式和普通Javascript数组很像,但是数据格式是一致和同一类型的(例如声音或者像素点的缓冲数据)。

类型数组的规范参见这里。这个规范实质上定义了一种arrayBuffer类型,相当于一个普通的定长二进制缓冲区。我们不能直接访问和操作arrayBuffer的内容,而需要类型数组来创建arrayBuffer的视图(从技术上来说,类型数组等同于arrayBuffer,因为它们本质上是一样的)。例如,要访问32位有符号整数数组作为缓冲区,会创建一个Int32Array的类型数组来指向arrayBuffer。

多个类型数组视图可以指向同一个arrayBuffer,采用不同的类型、不同的长度以及不同的位移。例如下面的代码:

var canvas = document.getElementById('canvas');
var canvasWidth  = canvas.width;
var canvasHeight = canvas.height;
var ctx = canvas.getContext('2d');
var imageData = ctx.getImageData(0,0, canvasWidth, canvasHeight);

var buf =new ArrayBuffer(imageData.data.length);
var data =new Uint32Array(buf);

for(var y =0; y < canvasHeight;++y){
for(var x =0; x < canvasWidth;++x){
var value = x * y &0xff;

data[y * canvasWidth + x]=
(255   <<24)|    // alpha
(value <<16)|    // blue
(value <<  8)|    // green
value;            // red
}
}

另外一方面,因为类型数组可以显著增加HTML5 Canvas 2D Web App的性能,所以这一特性对于使用HTML5来创建Web游戏的开发者会非常重要。

下面是两个使用类型数组的示例。

第一个是Energy2D的演示,用于对比普通数组和类型数组的性能,大家可以自行体验。



Energy2D演示

第二个示例是使用类型数组、FileAPI以及Web Workers实现的SHA1在线计算器,它的性能相当出色。正是在类型数组的支持下,Javascript执行SHA1、MD5这样复杂运算的速度变得越来越快。



类型数组支持的在线SHA1计算器

2、 二进制支持

上文曾经提到类型数组最主要的特点是支持二进制数据。的确,现在HTMl5的许多API涉及音视频和实时通信,这些功能经常依赖于二进制文件格式,例如MP3音频、MP4视频和PNG图像。二进制格式对于减少带宽,提高性能,以及与现有文件格式互相转换来说非常重要。

类型数组使得Web应用可以使用多种二进制文件格式和直接操作文件的二进制内容,例如从现有的媒体文件中提取数据。

在IE10上,已经提供了类型数组的支持(支持WebGL其实是微软非常纠结的事情)。我们可以看看微软所提供的二进制文件检测器的例子



在这个示例里,我们可以获取音乐文件的ID3头,视频文件的原始字节数据,以及附加文件的格式。它的核心代码如下:

imagedata = context.getImageData(0, 0, imagewidth,imageheight);

var canvaspixelarray = imagedata.data;

var canvaspixellen = canvaspixelarray.length;
var bytearray = new Uint8Array(canvaspixellen);

for (var i=0;i<canvaspixellen;++i) {
bytearray[i] = canvaspixelarray[i];
}


而把二进制数据还原为图像的代码如下,请注意我们不能直接从arrayBuffer获取数据直接放到Canvas中。

function sendphoto() {

imagedata = context.getImageData(0, 0, imagewidth,imageheight);

var canvaspixelarray = imagedata.data;

var canvaspixellen = canvaspixelarray.length;
var bytearray = new Uint8Array(canvaspixellen);

for (var i=0;i<canvaspixellen;++i) {
bytearray[i] = canvaspixelarray[i];
}

connection.send(bytearray.buffer);
context.fillStyle = '#ffffff';
context.fillRect(0, 0, imagewidth,imageheight);
}


疑问

理论上来看,类型数组的性能毫无疑问比普通数组更快,但是根据《现代浏览器里类型数组的性能》一文中的评测,可以看到某些操作和某些浏览器下,类型数组的性能反而更低,另外imageData和ArrayBuffer的性能在同一浏览器中还有不同的表现。这个现象让人困惑,因为imageData和ArrayBuffer其实就是为了性能敏感的功能诞生的,理论上能够提供更快的读取和写入速度。这有极大可能是目前浏览器厂商对于二进制数组优化不足造成的。我希望浏览器未来对于类型数组能有更好的支持。



某些操作和浏览器下,类型数组性能反而更低

总结

随着HTML5 Canvas、WebSocket等新特性的出现,WebApp能做的事情越来越多,同时Web App对于性能要求也越来越高,Javascript类型数组正是在这种情况下应运而生的。随着IE、Chrome、Opera等主流浏览器逐步提供对它的全面支持,以及可预期的性能优化,它将会发挥越来越重要的作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: