Javascript中的数组是个强大的家伙:
- 你可以创建的时候不规定长度,而是动态的去改变长度。
- 你可以把他当成普通的数组去读取,也可以当他是堆栈来使用。
- 你可以改变数组中每个元素的值甚至是类型。
好吧,其实他是一个对象,比如我们可以这样去创建数组:
1 | <font size= "3" > var array = new Array(10);</font>
|
Nach dem Login kopieren
Javascript的数组的强大以及全能,给我们带来了便捷性。但一般而言:
全能的东西能在各种环境下使用,但却不一定适用于各种环境。
而TypedArray正是为了解决Javascript中数组“干太多事”而出现的。
起源
TypedArray是一种通用的固定长度缓冲区类型,允许读取缓冲区中的二进制数据。
其在WEBGL规范中被引入用于解决Javascript处理二进制数据的问题。
TypedArray已经被大部分现代浏览器支持,例如可以用下面方法创建TypedArray:
1 2 3 4 5 6 7 8 9 10 11 | <font size= "3" >
var b = new ArrayBuffer(8);
var v1 = new Int32Array(b);
var v2 = new Uint8Array(b, 2);
var v3 = new Int16Array(b, 2, 2);</font>
|
Nach dem Login kopieren
则缓冲和创建的引用布局为:
变量 |
索引 |
|
字节数 |
b = |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
|
索引数 |
v1 = |
0 |
1 |
v2 = |
|
0 |
1 |
2 |
3 |
4 |
5 |
v3 = |
|
0 |
1 |
|
这表示Int32类型的v1数组的第0个元素是ArrayBuffer类型的b的第0-3个字节,如此等等。
构造函数
上面我们通过ArrayBuffer来创建TypedArray,而实际上,TypedArray提供了3个构造函数来创建他的实例。
构造函数 |
-
TypedArray(unsigned long length)
- 创建一个新的TypedArray,length是其固定长度。
|
-
TypedArray(TypedArray array)
-
TypedArray(type[] array)
- 创建一个新的TypedArray,其每个元素根据array进行初始化,元素进行了相应的类型转换。
|
-
TypedArray(ArrayBuffer buffer, optional unsigned
long byteOffset, optional unsigned long length)
- 创建一个新的TypedArray,使其作为buffer的一个引用,byteOffset为其起始的偏移量,length为其长度。
|
所以通常我们用下面的方式创建TypedArray:
1 | <font size= "3" > var array = new Uint8Array(10);</font>
|
Nach dem Login kopieren
或者:
1 | <font size= "3" > var array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);</font>
|
Nach dem Login kopieren
数据操作
TypedArray提供了setter、getter、set和subarray四个方法进行数据操作。
方法 |
- getter type get(unsigned long index)
-
返回指定索引的元素。
|
- setter void set(unsigned long
index, type value)
-
设置指定索引的元素为指定值。
|
- void set(TypedArray array, optional
unsigned long offset)
- void set(type[] array, optional unsigned
long offset)
-
根据array设置值,offset为偏移位置。
|
-
TypedArray subarray(long begin, optional
long end)
-
返回一个新的TypedArray,起始位为begin,结束位为end。
|
例如读取元素可以用:
1 2 | <font size= "3" > var array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
alert( array [4]);
|
Nach dem Login kopieren
设置元素可以用:
1 2 3 4 | <font size= "3" > var array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
alert( array [4]);
array [4] = 12;
alert( array [4]);
|
Nach dem Login kopieren
获取一个副本可以用:
1 2 | <font size= "3" > var array = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var array2 = array .subarray(0);</font>
|
Nach dem Login kopieren
数组类型
类型 |
大小 |
描述 |
Web IDL类型 |
C 类型 |
Int8Array |
1 |
8位有符号整数 |
byte |
signed char |
Uint8Array |
1 |
8位无符号整数 |
octet |
unsigned char |
Uint8ClampedArray |
1 |
8位无符号整数 (clamped) |
octet |
unsigned char |
Int16Array |
2 |
16位有符号整数 |
short |
short |
Uint16Array |
2 |
16位无符号整数 |
unsigned short |
unsigned short |
Int32Array |
4 |
32位有符号整数 |
long |
int |
Uint32Array |
4 |
32位无符号整数 |
unsigned long |
unsigned int |
Float32Array |
4 |
32位IEEE浮点数 |
unrestricted float |
float |
Float64Array |
8 |
64位IEEE浮点数 |
unrestricted double |
double |
玩过canvas的可能会觉得很眼熟。
因为ImageData中用于存储图像数据的数组便是Uint8ClampedArray类型的。
例如:
1 2 3 4 5 | <font size= "3" > var context = document.createElement( "canvas" ).getContext( "2d" );
var imageData = context.createImageData(100, 100);
console.log(imageData.data);</font>
|
Nach dem Login kopieren
其在FireBug中显示为:
Uint8ClampedArray { 0=0, 1=0, 2=0, 更多...}
为什么要用TypedArray
我们知道Javascript中数字是64位浮点数。则对于一个二进制图片(图片每个像素点是以8位无符号整数存储的),如果要将其数据在Javascript数组中使用,相当于使用了图片8倍的内存来存储一个图片的数据,这显然是不科学的。而TypedArray能帮助我们只使用原来1/8的内存来存储图片数据。
或者对于WebSocket,如果用base64进行传输也是一个花费较高的方式,转而使用二进制传送可能是更好的方式。
当然,TypedArray还有更多好处,比如具有更好的性能,下面我们进行一些小测试来验证这一点。
参与测试的浏览器为:
FireFox 17.0.1 和 Chrome 23.0.1271.97m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | <font size= "3" > var timeArray1 = [];
var timeArray2 = [];
function check1(){
var array = new Uint8ClampedArray(5000000);
for ( var i = array .length; i--;){
array [i] = Math. floor (Math.random() * 100);
}
var temp;
var time1 = ( new Date ()).getTime();
for ( var i = array .length; i--;){
temp = array [i];
}
var time2 = ( new Date ()).getTime();
console.log(time2 - time1);
timeArray1.push(time2 - time1);
}
function check2(){
var array2 = new Array(5000000);
for ( var i = array2.length; i--;){
array2[i] = Math. floor (Math.random() * 100);
}
var temp;
var time3 = ( new Date ()).getTime();
for ( var i = array2.length; i--;){
temp = array2[i];
}
var time4 = ( new Date ()).getTime();
console.log(time4 - time3);
timeArray2.push(time4 - time3);
}
function timer(__fun, __time, __callback){
var now = 0;
function begin(){
var timeout = setTimeout( function (){
if (now !== __time){
now++;
__fun();
begin();
} else {
if (timeArray1.length && timeArray2.length){
console.log( "timeArray1 == " + timeArray1 + ", average == " + average(timeArray1));
console.log( "timeArray2 == " + timeArray2 + ", average == " + average(timeArray2));
}
__callback && __callback();
}
}, 100);
}
begin();
}
function average(__array){
var total = 0;
for ( var i = __array.length; i--;){
total += __array[i];
}
return (total / __array.length);
}
timer(check1, 10, function (){
timer(check2, 10);
});</font>
|
Nach dem Login kopieren

可见Uint8ClampedArray的读取速度明显比Array要快(条状柱越长,代表花费时间越多)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <font size= "3" >
function check1(){
var array = new Uint8ClampedArray(5000000);
for ( var i = array .length; i--;){
array [i] = Math. floor (Math.random() * 100);
}
var temp;
var time1 = ( new Date ()).getTime();
for ( var i = array .length; i--;){
temp = array [Math. floor (Math.random() * 5000000)];
}
var time2 = ( new Date ()).getTime();
console.log(time2 - time1);
timeArray1.push(time2 - time1);
}
function check2(){
var array2 = new Array(5000000);
for ( var i = array2.length; i--;){
array2[i] = Math. floor (Math.random() * 100);
}
var temp;
var time3 = ( new Date ()).getTime();
for ( var i = array2.length; i--;){
temp = array2[Math. floor (Math.random() * 5000000)];
}
var time4 = ( new Date ()).getTime();
console.log(time4 - time3);
timeArray2.push(time4 - time3);
}
|
Nach dem Login kopieren

随即读取中Uint8ClampedArray的读取速度也是比Array要快的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <font size= "3" >
function check1(){
var array = new Uint8ClampedArray(5000000);
var time1 = ( new Date ()).getTime();
for ( var i = array .length; i--;){
array [i] = Math. floor (Math.random() * 100);
}
var time2 = ( new Date ()).getTime();
console.log(time2 - time1);
timeArray1.push(time2 - time1);
}
function check2(){
var array2 = new Array(5000000);
var time3 = ( new Date ()).getTime();
for ( var i = array2.length; i--;){
array2[i] = Math. floor (Math.random() * 100);
}
var time4 = ( new Date ()).getTime();
console.log(time4 - time3);
timeArray2.push(time4 - time3);
}
|
Nach dem Login kopieren

- Test4:复制操作(U8C to U8C 和 Array to U8C)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <font size= "3" >
function check1(){
var array = new Uint8ClampedArray(5000000);
for ( var i = array .length; i--;){
array [i] = Math. floor (Math.random() * 100);
}
var temp;
var array2 = new Uint8ClampedArray(5000000);
var time1 = ( new Date ()).getTime();
array2.set( array );
var time2 = ( new Date ()).getTime();
console.log(time2 - time1);
timeArray2.push(time2 - time1);
}
function check2(){
var array = new Array(5000000);
for ( var i = array .length; i--;){
array [i] = Math. floor (Math.random() * 100);
}
var temp;
var array2 = new Uint8ClampedArray(5000000);
var time1 = ( new Date ()).getTime();
array2.set( array );
var time2 = ( new Date ()).getTime();
console.log(time2 - time1);
timeArray2.push(time2 - time1);
}
|
Nach dem Login kopieren

可见U8C复制到U8C,比Array复制到U8C快得多。
参考文献
Typed Array Specification