摘要:以上每個構造函數都對應如下形式的參數可以指定序列化其中到部分的二進制數據。的構造函數還接受另一個作為參數,開辟新內存復制其值,對原數組不構成影響,也不共用內存。
這個部分如果沒有C語言和計算機基礎會比較難理解,如果實在理解不了可以收藏它,日后再看。
二進制數組其實很早就有了,不過為了 WebGL 中,數據可以高效和顯卡交換數據。分為3類:
ArrayBuffer:代表內存中的一段二進制數據;
TypedArray:讀寫簡單的二進制數據,如 Uint8Array, Int16Array, Float32Array 等9類;
DataView:讀寫復雜的二進制數據,如 Uint8, Int16, Float32 等8類;
數據類型 | 字節長度 | 含義 | 對應 C 語言類型 | TypedArray 類型 | DataView 類型 |
---|---|---|---|---|---|
Int8 | 1 | 8位有符號整數 | char | Int8Array | Int8 |
Uint8 | 1 | 8位無符號整數 | unsigned char | Uint8Array | Uint8 |
Uint8C | 1 | 8位無符號整數(自動過濾溢出) | unsigned char | Uint8ClampedArray | 不支持 |
Int16 | 2 | 16位有符號整數 | short | Int16Array | Int16 |
Uint16 | 2 | 16位無符號整數 | unsigned short | Uint16Array | Uint16 |
Int32 | 4 | 32位有符號整數 | int | Int32Array | Int32 |
Uint32 | 4 | 32位無符號整數 | unsigned int | Uint32Array | Uint32 |
Float32 | 4 | 32位浮點數 | float | Float32Array | Float32 |
Float64 | 8 | 64位浮點數 | double | Float64Array | Float64 |
ArrayBuffer 代表內存中的一段二進制數據,我們沒法直接操作,需要利用視圖(TypedArray,DataView)按一定格式解讀二進制數據。但我們依然可以構造一段內存來存放二進制數據:
var buf = new ArrayBuffer(32); //分配32個字節的內存存放數據, 默認全0 var dataview = new DataView(buf); //將這段內存轉為視圖 dataview.getUint8(0); //得到第一個8字節的值(無符號),0
這里需要強調的是,分配內存空間不要太大!畢竟你的內存是有限的。
其次,無論使用什么視圖,其實例化的內存如果共享,所有的寫入操作會修改每一個視圖,因為內存共用的:
var buf = new ArrayBuffer(32); var view16 = new Int16Array(buf); var viewu8 = new Uint8Array(buf); console.log(viewu8[0]); //0 view16[0]=-1; console.log(viewu8[0]); //255
這里之所以得到255,是因為內存共用導致的,但為何不是-1?Int16Array 是有符號類型的,這樣二進制的最高位用作符號位,負數記為1:1000 0000 0000 0001,之后的數字用移碼存儲,得到-1的二進制為:1111 1111 1111 1111, 之后利用Uint8Array讀取無符號的前8位,得到1111 1111這個計算為十進制為 $2^8-1=255$。具體關于數制轉換和反碼補碼這里不再展開,否則就跑偏了。
ArrayBuffer 對象也有幾個方法和屬性:
byteLength: 得到內存區域的字節長度
const N = 32; var buf = new ArrayBuffer(N); if(buf.byteLength === N){ //分配成功 } else { //分配失敗 }
slice(start=0, end=this.byteLength): 分配新內存,并把先有內存 start 到 end 部分復制過去,返回這段新內存區域
var buf = new ArrayBuffer(32); var newBuf = buf.slice(0,3);
isView(view): 判斷傳入的 view 是否當前 buffer 的視圖,是則返回 true, 否則 false。該方法暫無法使用。
var buf1 = new ArrayBuffer(32); var buf2 = new ArrayBuffer(32); var buf1View = new Int8Array(buf1); var buf2View = new Int8Array(buf2); buf1.isView(buf1View); //true buf1.isView(buf2View); //falseTypedArray
具有一個構造函數 DataView(), 接受一個ArrayBuffer參數,視圖化該段內存;或接受一個數組參數,實例化該數組為二進制內容。得到的值是一個數組,可以直接使用[]訪問每個位置的內容,有length屬性。其構造函數有9個:
數據類型 | 字節長度 | 含義 | 對應 C 語言類型 | TypedArray 類型構造函數 |
---|---|---|---|---|
Int8 | 1 | 8位有符號整數 | char | Int8Array() |
Uint8 | 1 | 8位無符號整數 | unsigned char | Uint8Array() |
Uint8C | 1 | 8位無符號整數(自動過濾溢出) | unsigned char | Uint8ClampedArray() |
Int16 | 2 | 16位有符號整數 | short | Int16Array() |
Uint16 | 2 | 16位無符號整數 | unsigned short | Uint16Array() |
Int32 | 4 | 32位有符號整數 | int | Int32Array() |
Uint32 | 4 | 32位無符號整數 | unsigned int | Uint32Array() |
Float32 | 4 | 32位浮點數 | float | Float32Array() |
Float64 | 8 | 64位浮點數 | double | Float64Array() |
以上9個會對內存進行不同位數的格式化,以得到對應類型值的數組。這個數組不同于普通數組,它不支持稀疏數組,默認值為0,而且同一個數組只能存放同一個類型的變量。
以上每個構造函數都對應如下形式的參數:
(buffer, start=0, len=buffer.byteLength-start*8)
可以指定序列化其中 start到 end部分的二進制數據。注意這里指定的范圍必須和數組類型所匹配,不能出現類似new Int32Array(buffer,2,2)的情況。如果你覺得這個不符合你的需求,可以使用 DataView。
如果你覺得上面的寫法復雜,可以不寫 new ArrayBuffer,直接使用 TypedArray,但注意參數的意義不一樣:
var f64a = new Float64Array(4); //分配32個字節,并作為double類型使用。 32 = 64 / 8 * 4
TypedArray的構造函數還接受另一個TypedArray作為參數,開辟新內存復制其值并改變類型,對原視圖和buffer 不構成影響,也不共用內存。
TypeArray的構造函數還接受另一個Array作為參數,開辟新內存復制其值,對原數組不構成影響,也不共用內存。
當然利用一下方法,可以把 TypedArray 轉換為普通數組:
var arr = [].slice.call(typedArray);
TypedArray具有除了concat()以外的全部數組方法,當然,它也具有 iterator,可以用 for...of 遍歷。
以下是 TypedArray 特有的屬性和方法:
buffer屬性:返回該視圖對于的二進制內存區域
BYTES_PER_ELEMENT屬性:是個常數,表示數組中每個值的字節大小,不同視圖的返回值與上方表格一致
byteLength: 返回該視圖對于的內存大小,只讀
byteOffset: 返回該視圖從對應 buffer 的哪個字節開始,只讀
set(arr_or_typeArray, start=0): 在內存層面,從arr_or_typeArray 的 start 下標開始復制數組到當然 typeArray
subarray(start=0,end=this.length),截取 start到 end部分子數組,但是和原數組共用內存
from(): 接受一個可遍歷參數,轉為該視圖實例
of(): 將參數列表轉為該視圖實例
小技巧,轉換字符串和 ArrayBuffer
//該方法僅限轉換 utf-16 的字符串 function ab2str(buf){ return String.fromCharCode.apply(null, new Uint16Array(buf)); } function str2ab(str){ var len = str.length; var view = new Uint16Array(len); for(let i = 0; i < len; i++){ view[i] = str.charCodeAt(i); } return view.buffer; } var str = "Hello world"; var buf = str2ab(str); var view = new Uint16Array(buf); for(var i = 0; i < view.length; i++){ console.log(String.fromCharCode(view[i])); //一次輸出"Hello world"的每個字母 } console.log(ab2str(buf)); //"Hello world"
這里擴展一些編碼知識,我們知道計算機里面存儲的是二進制,并且存儲的最小單位是字節。但是不同的系統存儲方式不同,分為高位優先和低位優先。比如 20170101 這個數字,其十六進制表示為 0x0133C575, 在低位優先的系統中存儲方式為 0x75 0xC5 0x33 0x01, 而在高位優先的系統中存儲方式為 0x01 0x33 0xC5 0x75。由于大多數計算機采用低位優先的方式,所以 ES6 采用是也是低位優先的方式,但遇到高位優先的數據時,就不能簡單的直接那來使用,具體使用會在 DataView 中介紹,這里說明一種判斷低位優先(little endian)還是高位優先(big endian)的方法:
還有需要注意的是數據溢出,這個也是需要數制方面基礎比較好理解,這里不過多展開了。舉一個例子:
Uint8 只能表示8位無符號整數,最大是1111 1111, 也就是十進制的 0~255;Int8因為有了符號位,只能表示十進制-128~127,如果給它的值不在這個范圍內就會發生溢出,得到一個你意想不到但情理之中的值
var view1 = new Uint8Array(2); view1[0] = 256; //256 二進制是 1 0000 0000 由于數據只能容納8個值,進位1就丟了 view1[1] = -1; //之前說過-1 二進制(補碼)為 1111 1111(全1), 作為無符號數8個1就是255 console.log(view1[0]); //0 console.log(view1[1]); //255 var view2 = new Int8Array(2); view2[0] = 128; //由于符號位溢出,系統自動用32位計算這個數1 000 0000 0000 0000 0000 0000 1000 0000,取符號位和最后8位得到-128 view2[1] = -128; //由于符號位溢出,系統自動用32位計算這個數0 111 1111 1111 1111 1111 1111 0111 1111,取符號位和最后8位得到127 console.log(view2[0]); //-128 console.log(view2[1]); //127
為了防止這樣的情況,js 有一個 Unit8ClampedArray, 使整數方向的溢出值為255,0方向的易楚志為0。注意這是個無符號的類型;
var view = new Uint8ClampedArray(2); view[0] = 256; view[1] = -1; console.log(view[0]); //255 console.log(view[1]); //0復合視圖
劃分一塊 buffer 使用得到 C 語言中的結構體
var buf = new ArrayBuffer(24); var name = new Uint8Array(buf, 0, 16); var gender = new Uint8Array(buf, 16, 1); var age = new Uint16Array(buf, 18, 1); var score = new Float32Array(buf,20,1);
相當于以下 C語言代碼
struct Person{ char name[16]; char gender; int age; float score; }
共用一塊 buffer 使用得到 C 語言中的聯合體
var buf = new ArrayBuffer(8); var num = new Uint16Array(buf); var dotNum = new Float64Array(buf);
相當于以下 C語言代碼
union Example{ int num[4]; double dotNum; }DataView
具有一個構造函數 DataView(), 接受一個ArrayBuffer參數,視圖化該段內存。畢竟當一段內存有多種數據時,復合視圖也不是那么方便,這時適合使用 DataView 視圖。其次 DataView 可以自定義高位優先和低位優先,這樣可以讀取的數據就更多了。
DataView構造函數形式如下,這一點和 TypedArray 一致:
(buffer, start=0, len=buffer.byteLength-start*8)
它具有以下方法格式化讀取 buffer 中的信息:
getInt8(start, isLittleEndian): 從 start 字節處讀取 1 個字節,返回一個8位有符號整數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getUint8(start, isLittleEndian): 從 start 字節處讀取 1 個字節,返回一個8位無符號整數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getInt16(start, isLittleEndian): 從 start 字節處讀取 2 個字節,返回一個16位有符號整數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getUint16(start, isLittleEndian): 從 start 字節處讀取 2 個字節,返回一個16位無符號整數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getInt32(start, isLittleEndian): 從 start 字節處讀取 4 個字節,返回一個32位有符號整數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getUint32(start, isLittleEndian): 從 start 字節處讀取 4 個字節,返回一個32位無符號整數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getFloat32(start, isLittleEndian): 從 start 字節處讀取 4 個字節,返回一個32位浮點數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
getFloat64(start, isLittleEndian): 從 start 字節處讀取 8 個字節,返回一個64位浮點數, 第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
它具有以下方法格式化寫入 buffer 中的信息:
setInt8(start,value,isLittleEndian): 在 start位置寫入 1 個字節的8位有符號整數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setUint8(start,value,isLittleEndian): 在 start位置寫入 1 個字節的8位無符號整數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setInt16(start,value,isLittleEndian): 在 start位置寫入 2 個字節的16位有符號整數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setUint16(start,value,isLittleEndian): 在 start位置寫入 2 個字節的16位無符號整數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setInt32(start,value,isLittleEndian): 在 start位置寫入 4 個字節的32位有符號整數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setUint32(start,value,isLittleEndian): 在 start位置寫入 4 個字節的32位無符號整數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setFloat32(start,value,isLittleEndian): 在 start位置寫入 4 個字節的32位浮點數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
setFloat64(start,value,isLittleEndian): 在 start位置寫入 8 個字節的64位浮點數value;第二參默認為 false 表示使用高位優先,為 true 表示低位優先;
它具有以下屬性和方法:
buffer屬性:返回該視圖對于的二進制內存區域
byteLength: 返回該視圖對于的內存大小,只讀
byteOffset: 返回該視圖從對應 buffer 的哪個字節開始,只讀
如果你不知道計算機使用的是高位優先還是低位優先,也可以自行判斷:
//方法1 const BIG_ENDIAN = Symbol("BIG_ENDIAN"); const LITTLE_ENDIAN = Symbol("LITTLE_ENDIAN"); function getPlatformEndianness(){ let arr32 = Uint32Array.of(0x12345678); let arr8 = new Uint8Array(arr32.buffer); switch((arr8[0]*0x1000000)+(arr8[1]*0x10000)+(arr8[2]*0x100)+arr8[3]){ case 0x12345678: return BIG_ENDIAN; case 0x78563412: return LITTLE_ENDIAN; default: throw new Error("unknow Endianness"); } } //方法2 window.isLittleEndian = (function(){ var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256; }());
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97450.html
摘要:二進制和八進制表示法提供了二進制和八進制數值的新的寫法,分別用前綴或和或表示。用來檢查是否為有窮以及是否為這兩個新方法只對數值有效,非數值一律返回。引入了和這兩個常量,用來表示這個范圍的上下限。因為有精度限制,超過的次方的值無法精確表示。 1 二進制和八進制表示法 ES6提供了二進制和八進制數值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示。 console.log(0b10...
摘要:吉字符串的遍歷器接口為字符串添加了遍歷器接口,使得字符串可以被循環遍歷。提供字符串實例的方法,用來將字符的不同表示方法統一為同樣的形式,這稱為正規化。返回布爾值,表示參數字符串是否在源字符串的頭部。 1 字符串的Unicode表示法 ES6 只要將碼點放入大括號,就能正確解讀該字符; var x = u20bb7; document.write(x); //?7 var x = u{2...
摘要:數組的解構賦值允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構。如果變量名與屬性名不一致,必須寫成下面這樣。 1 數組的解構賦值 ES6允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構(Destructuring)。 基本用法 { var [a,[b,c],d,,...f] = [1,[2,3],4,5,6,7]; console...
摘要:解構賦值解構賦值簡單來說就是對應位置數組或對應鍵名對象的變量匹配過程。字符串集合使用結構賦值實現疊加并交換變量對象的解構賦值對象的解構賦值與變量位置次序無關只取決于鍵名是否嚴格相等。 解構賦值 解構賦值簡單來說就是 對應位置(數組)或對應鍵名(對象)的變量匹配過程。如果匹配失敗, 對于一般變量匹配不到結果就是 undefined, 對于具有展開運算符(...)的變量結果就是空數組。 數...
摘要:原來的也被修改了數組實例的喝方法,用于找出第一個符合條件的數組成員。它的參數是一個回調函數,所有數組成員依次執行該回調函數,直到找出第一個返回值為的成員,然后返回該成員。數組實例的方法使用給定值,填充一個數組。 1 Array.from() Array.from方法用于將兩類對象轉為真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括...
閱讀 2601·2021-09-26 10:17
閱讀 3228·2021-09-22 15:16
閱讀 2136·2021-09-03 10:43
閱讀 3266·2019-08-30 11:23
閱讀 3662·2019-08-29 13:23
閱讀 1308·2019-08-29 11:31
閱讀 3691·2019-08-26 13:52
閱讀 1400·2019-08-26 12:22