? ? ? 我在Chrome的[最新動態](http://blog.csdn.net/hfahe/article/details/7408426)里提到了Typed Arrays(Typed Array,類型數組)這個概念,可能對很多人來說非常陌生,那么它是什么,又有什么用途呢?
**之前的問題**
? ? ??Web應用程序變得越來越強大,例如新增了音視頻處理、WebSocket等多個功能特性。毫無疑問,如果Javascript能夠快速方便的操作原始二進制數據會相當的有用。過去,我們必須要把原始數據當作字符串來處理,并且使用charCodeAt方法來從數據緩沖區中讀取字節。
? ? ??但是這種方法需要多次轉換數據(尤其在二進制數據不是字節格式的數據時,例如32位整數或者浮點數),所以非常慢而且容易出錯。
? ? ??Javascript需要一種機制來更有效的訪問原始的二進制數據,由此產生了類型數組。
**定義**
? ? ??其實除了Javascript,類型數組在其他很多語言中也有。它是一種數組,只有一種變量的類型。例如,一個float類型的數組將只包含浮點數而不能混用字符串和浮點數。此外,一個類型數組在初始化后不能改變大小。它看起來形式和普通Javascript數組很像,但是數據格式是一致和同一類型的(例如聲音或者像素點的緩沖數據)。
? ? ??類型數組的規范參見[這里](http://www.khronos.org/registry/typedarray/specs/latest/)。這個規范實質上定義了一種arrayBuffer類型,相當于一個普通的定長二進制緩沖區。我們不能直接訪問和操作arrayBuffer的內容,而需要類型數組來創建arrayBuffer的視圖(從技術上來說,類型數組等同于arrayBuffer,因為它們本質上是一樣的)。例如,要訪問32位有符號整數數組作為緩沖區,會創建一個Int32Array的類型數組來指向arrayBuffer。
? ? ??多個類型數組視圖可以指向同一個arrayBuffer,采用不同的類型、不同的長度以及不同的位移。例如下面的代碼:
~~~
// 創建一個8字節的ArrayBuffer
var b = new ArrayBuffer(8);
// 創建一個指向b的視圖v1,采用Int32類型,開始于默認的字節索引0,直到緩沖區的末尾
var v1 = new Int32Array(b);
// 創建一個指向b的視圖v2,采用Uint8類型,開始于字節索引2,直到緩沖區的末尾
var v2 = new Uint8Array(b, 2);
// 創建一個指向b的視圖v3,采用Int16類型,開始于字節索引2,長度為2
var v3 = new Int16Array(b, 2, 2);
~~~
? ? ??上述代碼里變量的數據結構如下所示。

變量的數據結構
? ? ??類型數組包括以下幾種類型:
<table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" style="background:#4F81BD;"><p align="left"><strong>名稱</strong></p></td> <td valign="top" style="background:#4F81BD;"><p align="left"><strong>大小 (以字節為單位)</strong></p></td> <td valign="top" style="background:#4F81BD;"><p align="left"><strong>說明</strong></p></td> </tr><tr><td valign="top"><p align="left"><strong>Int8Array</strong></p></td> <td valign="top"><p align="left">1</p></td> <td valign="top"><p align="left">8位有符號整數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Uint8Array</strong></p></td> <td valign="top"><p align="left">1</p></td> <td valign="top"><p align="left">8位無符號整數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Int16Array</strong></p></td> <td valign="top"><p align="left">2</p></td> <td valign="top"><p align="left">16位有符號整數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Uint16Array</strong></p></td> <td valign="top"><p align="left">2</p></td> <td valign="top"><p align="left">16位無符號整數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Int32Array</strong></p></td> <td valign="top"><p align="left">4</p></td> <td valign="top"><p align="left">32位有符號整數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Uint32Array</strong></p></td> <td valign="top"><p align="left">4</p></td> <td valign="top"><p align="left">32位無符號整數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Float32Array</strong></p></td> <td valign="top"><p align="left">4</p></td> <td valign="top"><p align="left">32位浮點數</p></td> </tr><tr><td valign="top"><p align="left"><strong>Float64Array</strong></p></td> <td valign="top"><p align="left">8</p></td> <td valign="top"><p align="left">64位浮點數</p></td> </tr></tbody></table>
? ? ??類型數組實際上目前是作為WebGL的一部分來實現的(和它相關的還有File API),但是它可以用在任何地方。
? ? ??下面我們來談談類型數組的優點和用途。
**優點**
1、? 性能優秀
? ? ??所有類型數組相關的文檔都提到的重要一點是,類型數組比傳統數組快的多,具有非常好的性能。因為類型數組實際上是作為一個固定的內存塊來進行訪問的,而傳統的普通Javascript數組使用的是Hash查找方式(因為元素長度不定)。
? ? ??這里有一個簡單的測試結果,在Firefox4 Beta1版本,我們對比了一個普通數組和Float32Array數組在操作1億個元素時每種操作所花費的時間。這個測試運行在Win7 64位、4G內存和Intel雙核1.3G CPU的平臺上。我們運行這個測試8次并使用其中最慢的一個時間。需要指出的是,普通Javascript數組的寫入操作經常花費超過10秒鐘,這會導致出現運行緩慢的腳本對話框。
<table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" style="background:#4F81BD;"><p><strong><span style="color:white;">操作</span></strong></p></td> <td valign="top" style="background:#4F81BD;"><p><strong><span style="color:white;">普通數組</span></strong></p></td> <td valign="top" style="background:#4F81BD;"><p><strong><span style="color:white;">Float32Array</span></strong></p></td> </tr><tr><td valign="top"><p><strong>寫</strong></p></td> <td valign="top"><p>8947</p></td> <td valign="top"><p>1455</p></td> </tr><tr><td valign="top"><p><strong>讀</strong></p></td> <td valign="top"><p>1948</p></td> <td valign="top"><p>1109</p></td> </tr><tr><td valign="top"><p><strong>循環復制</strong></p></td> <td valign="top"><p>>10,000</p></td> <td valign="top"><p>1969</p></td> </tr><tr><td valign="top"><p><strong>片段復制</strong></p></td> <td valign="top"><p>1125</p></td> <td valign="top"><p>503</p></td> </tr></tbody></table>
? ? ??下面我們有一個關于普通數組、類型數組(arrayBuffer)以及imageData之間性能的比較,可以看到arrayBuffer會快得多。

性能優異的arrayBuffer
? ? ??其實在類型數組之前,Javascript也支持二進制字節的數組,這就是imageData。imageData是Canvas元素2D上下文環境里定義的數據類型。當在Canvas 2D里調用getImageData或者createImageData方法時就創建了imageData。imageData的data屬性是一個字節數組,它實際大小是圖片寬*高的四倍(因為每個像素有R、G、B、A四個通道)。之前我們在《[用HTML5創建超酷圖像灰度漸變效果](http://blog.csdn.net/hfahe/article/details/6208765)》這篇文章里就用到了imageData。
? ? ??從圖表數據來看,imageData和arrayBuffer在多個瀏覽器里創建的性能遠遠高于普通的數組([數據來源](http://blog.n01se.net/?p=248))。不過這里有一個問題,后面將會提到。
? ? ??可以想見,因為優秀的性能表現,類型數組可以廣泛的應用于Javascript圖像以及視頻的處理和壓縮,還有一些需要復雜運算的場景例如MD5計算中,讓功能可以更快速更高效的完成。例如我們先把imageData轉換為類型數組以換取更快的執行速度,如下面的代碼:
~~~
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的[演示](http://visual-demos.dev.concord.org/seasons/earth/model2d.html),用于對比普通數組和類型數組的性能,大家可以自行體驗。

Energy2D演示
? ? ??第二個示例是使用類型數組、FileAPI以及Web Workers實現的SHA1[在線計算器](http://antimatter15.github.com/js-typed-array-sha1/),它的性能相當出色。正是在類型數組的支持下,Javascript執行SHA1、MD5這樣復雜運算的速度變得越來越快。

類型數組支持的在線SHA1計算器
2、? 二進制支持
? ? ??上文曾經提到類型數組最主要的特點是支持二進制數據。的確,現在HTMl5的許多API涉及音視頻和實時通信,這些功能經常依賴于二進制文件格式,例如MP3音頻、MP4視頻和PNG圖像。二進制格式對于減少帶寬,提高性能,以及與現有文件格式互相轉換來說非常重要。
? ? ??類型數組使得Web應用可以使用多種二進制文件格式和直接操作文件的二進制內容,例如從現有的媒體文件中提取數據。
? ? ??在IE10上,已經提供了類型數組的支持(支持WebGL其實是微軟非常糾結的事情)。我們可以看看微軟所提供的二進制文件檢測器的[例子](http://ie.microsoft.com/testdrive/HTML5/TypedArrays/):

? ? ??在這個示例里,我們可以獲取音樂文件的ID3頭,視頻文件的原始字節數據,以及附加文件的格式。它的核心代碼如下:
~~~
function getHexChunk(buffer, startAt) {
var chunkLength = Math.min(CHUNK_SIZE, buffer.byteLength - startAt)
var uints = new Uint8Array(buffer, startAt, chunkLength);
var rowString = "";
for (var row = 0; row < uints.length; row += 16) {
var remaining = uints.length - row;
rowString += intToHexString(row + startAt, 8);
rowString += " ";
for (var offset = 0; offset < 8 ; offset++) {
if (offset < remaining) rowString += intToHexString(uints[row + offset], 2) + " ";
else rowString += " ";
}
rowString += " ";
for (; offset < 16; offset++) {
if (offset < remaining) rowString += intToHexString(uints[row + offset], 2) + " ";
else rowString += " ";
}
rowString += " ";
for (var offset = 0; offset < 8; offset++) {
rowString += charForInt(uints[row + offset]);
}
for (; offset < 16; offset++) {
rowString += charForInt(uints[row + offset]);
}
rowString += "\n";
}
return rowString;
}
~~~
? ? ??頁面上文件的二進制格式輸出就是用這段代碼實現的。
**具體應用**
[? ? ? ??這里](http://blog.digitalbackcountry.com/2012/01/dealing-with-binary-data-from-a-canvas-object-using-javascript-typedarrays/)有一個使用類型數組在Canvas圖像和二進制數據之間互相轉換,然后通過WebSocket發送的示例。作者提到“在我實現二進制WebSocket示例時,我學習了很多Javascript類型數組的知識,了解了如何把對象轉換為二進制數據。我寫了一個示例來獲取Canvas圖像數據,并且把它通過二進制的WebSocket連接發送出去。WebSocket服務器獲取圖像數據,然后把它發送給所有連接的客戶端(宇捷:這讓我想起了最近國外非常火爆的超人氣應用[DrawSomething-你畫我猜](http://game.163.com/12/0322/17/7T7G2FL200314OSE.html),我們可以用這種方式實現類似的WebApp),然后客戶端再把Canvas數據還原為PNG圖片。采用這種方式發送圖像數據比起base64編碼來更有效率(數據小33%,而且更利于序列化和存儲)。”

創造了歷史的應用-你畫我猜
? ? ??WebSocket支持二進制數據傳輸,對于WebSocket服務器來說,使用二進制數據會比UTF-8更為簡單,不過現在瀏覽器支持方面還有問題。
? ? ??示例里實現將Canvas數據轉換為二進制格式的代碼如下:
~~~
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中。
~~~
var bytearray =new Uint8Array(event.data);
var tempcanvas = document.createElement('canvas');
tempcanvas.height= imageheight;
tempcanvas.width= imagewidth;
var tempcontext = tempcanvas.getContext('2d');
var imgdata = tempcontext.getImageData(0,0,imagewidth,imageheight);
var imgdatalen = imgdata.data.length;
for(var i=8;i<imgdatalen;i++)
{
imgdata.data[i]= bytearray[i];
}
tempcontext.putImageData(imgdata,0,0);
~~~
? ? ??在Adobe的官網上,也有一個類似的[完整示例](http://www.adobe.com/devnet/html5/articles/real-time-data-exchange-in-html5-with-websockets.html):《Real Time Data Exchange in HTML5 with WebSocket》。可以看到里面利用類型數組發送圖片的代碼如下:
~~~
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);
}
~~~
**疑問**
? ? ??理論上來看,類型數組的性能毫無疑問比普通數組更快,但是根據《[現代瀏覽器里類型數組的性能](http://blog.n01se.net/?p=248)》一文中的評測,可以看到某些操作和某些瀏覽器下,類型數組的性能反而更低,另外imageData和ArrayBuffer的性能在同一瀏覽器中還有不同的表現。這個現象讓人困惑,因為imageData和ArrayBuffer其實就是為了性能敏感的功能誕生的,理論上能夠提供更快的讀取和寫入速度。這有極大可能是目前瀏覽器廠商對于二進制數組優化不足造成的。我希望瀏覽器未來對于類型數組能有更好的支持。

某些操作和瀏覽器下,類型數組性能反而更低
**總結**
? ? ??隨著HTML5 Canvas、WebSocket等新特性的出現,WebApp能做的事情越來越多,同時Web App對于性能要求也越來越高,Javascript類型數組正是在這種情況下應運而生的。隨著IE、Chrome、Opera等主流瀏覽器逐步提供對它的全面支持,以及可預期的性能優化,它將會發揮越來越重要的作用。
**附:[類型數組的瀏覽器支持情況](http://caniuse.com/#feat=typedarrays)**
轉載請注明:來自[蔣宇捷的博客](http://blog.csdn.net/hfahe)
- 前言
- AutoPager的簡單實現
- 利用CSS3特性巧妙實現漂亮的DIV箭頭
- IE9在Win7下任務欄新特性簡介
- 瀏覽器九宮格的簡單實現
- Raphael js庫簡介
- 使用CSS3構建Ajax加載動畫
- 用CSS3創建動畫價格表
- 用CSS3實現瀏覽器的縮放功能
- 用純CSS3實現QQ LOGO
- 用CSS3創建旋轉載入器
- 使用Javascript開發移動應用程序
- 用HTML5創建超酷圖像灰度漸變效果
- 使用CSS3創建文字顏色漸變(CSS3 Text Gradient)
- 僅用CSS創建立體旋轉幻燈片
- 如何創建跨瀏覽器的HTML5表單
- 用CSS3實現動畫進度條
- HTML5 Guitar Tab Player
- 奇妙的HTML5 Canvas動畫實例
- 談HTML5和CSS3的國際化支持
- 實現跨瀏覽器的HTML5占位符
- 前端開發必備工具:WhatFont Bookmarklet-方便的查詢網頁上的字體
- 使用HTML5和CSS3來創建幻燈片
- HTML5之美
- 如何使用HTML5創建在線精美簡歷
- 以小見大、由淺入深-談如何面試Javascript工程師
- 快速入門:HTML5強大的Details元素
- 用CSS3實現圖像風格
- HTML5視頻字幕與WebVTT
- 用純CSS3實現Path華麗動畫
- 用3個步驟實現響應式網頁設計
- 遇見CSS3濾鏡
- 關于CSS3濾鏡的碎念
- 用純CSS3繪制萌系漫畫人物動態頭像
- CSS3新的鼠標樣式介紹
- 用HTML5獻上愛的3D玫瑰
- 對HTML5 Device API相關規范的解惑
- 如何使用HTML5實現拍照上傳應用
- 2012第一季度國外HTML5移動開發趨勢
- HTML5新特性:范圍樣式
- 百度開發者大會-《用HTML5新特性開發移動App》PPT分享
- Chrome 19對于HTML5最新支持的動態:電池狀態API,全屏API,震動API,語音API
- 遇見Javascript類型數組(Typed Array)
- 用HTML5 Audio API開發游戲音樂
- 用HTML5實現人臉識別
- 用Javascript實現人臉美容
- Chrome 20對于HTML5最新支持的動態:顏色輸入,網絡信息API,CSS著色器
- 用HTML5實現手機搖一搖的功能
- 用HTML5實現iPad應用無限平滑滾動
- 用非響應式設計構建跨端Web App
- 了解SVG
- HTML5圖像適配介紹
- HTML5安全:內容安全策略(CSP)簡介
- HTML5安全:CORS(跨域資源共享)簡介
- 用CSS3 Region和3D變換實現書籍翻頁效果
- 談談移動App的思維誤區
- Chrome新特性:文件夾拖拽支持
- 《關注HTML5安全》
- HTML5安全風險詳析之一:CORS攻擊
- HTML5安全風險詳析之二:Web Storage攻擊
- HTML5圖像適配最新進展:響應式圖片規范草案
- HTML5移動Web App相關標準狀態及路線圖
- HTML5安全風險詳析之三:WebSQL攻擊
- Chrome引入WebRTC支持視頻聊天App
- HTML5安全風險詳析之四:Web Worker攻擊
- HTML5安全風險詳析之五:劫持攻擊
- HTML5安全風險詳析之六:API攻擊
- HTML5安全攻防詳析之七:新標簽攻擊
- 在iOS Safari中播放離線音頻
- 使用WebRTC實現遠程屏幕共享
- Firefox、Android、iOS遇見WebRTC
- HTML5光線傳感器簡介
- HTML5安全攻防詳析之八:Web Socket攻擊
- HTML5安全攻防詳析之完結篇:HTML5對安全的改進
- 激動人心!在網頁上通過語音輸入文字 - HTML5 Web Speech API介紹
- Web滾動性能優化實戰
- 用CSS3設計響應式導航菜單
- 用HTML5構建高性能視差網站
- 漫談@supports與CSS3條件規則
- HTML5下載屬性簡介
- 如何開發優秀的HTML5游戲?-迪斯尼《尋找奧茲之路》游戲技術詳解(一)
- 如何開發優秀的HTML5游戲?-迪斯尼《尋找奧茲之路》游戲技術詳解(二)
- 趨勢:Chrome為打包應用提供強大新特性
- 從HTML5移動應用現狀談發展趨勢
- 基于HTML5的Web跨設備超聲波通信方案