1
+ /**
2
+
3
+ 第一种允许你读写 ArrayBuffer 的视图是 DataView。这个视图专为文件 I/O 和网络 I/O 设计,其API 支持对缓冲数据的高度控制
4
+
5
+ DataView 对缓冲内容没有任何预设,也不能迭代。
6
+
7
+ 必须在对已有的 ArrayBuffer 读取或写入时才能创建 DataView 实例。这个实例可以使用全部或部分 ArrayBuffer,且维护着对该缓冲实例的引用,以及视图在缓冲中开始的位置。
8
+
9
+ const buf = new ArrayBuffer(16);
10
+ // DataView 默认使用整个 ArrayBuffer
11
+ const fullDataView = new DataView(buf);
12
+ alert(fullDataView.byteOffset); // 0
13
+ alert(fullDataView.byteLength); // 16
14
+ alert(fullDataView.buffer === buf); // true
15
+
16
+
17
+ // 构造函数接收一个可选的字节偏移量和字节长度
18
+ // byteOffset=0 表示视图从缓冲起点开始
19
+ // byteLength=8 限制视图为前 8 个字节
20
+ const firstHalfDataView = new DataView(buf, 0, 8);
21
+ alert(firstHalfDataView.byteOffset); // 0
22
+ alert(firstHalfDataView.byteLength); // 8
23
+ alert(firstHalfDataView.buffer === buf); // true
24
+
25
+
26
+ // 如果不指定,则 DataView 会使用剩余的缓冲
27
+ // byteOffset=8 表示视图从缓冲的第 9 个字节开始
28
+ // byteLength 未指定,默认为剩余缓冲
29
+ const secondHalfDataView = new DataView(buf, 8);
30
+ alert(secondHalfDataView.byteOffset); // 8
31
+ alert(secondHalfDataView.byteLength); // 8
32
+ alert(secondHalfDataView.buffer === buf); // true
33
+
34
+
35
+ 要通过 DataView 读取缓冲,还需要几个组件。
36
+
37
+ 首先是要读或写的字节偏移量。可以看成 DataView 中的某种“地址”。
38
+ DataView 应该使用 ElementType 来实现 JavaScript 的 Number 类型到缓冲内二进制格式的转换。
39
+ 最后是内存中值的字节序。默认为大端字节序。
40
+
41
+ */
42
+
43
+ // 1. ElementType
44
+
45
+ /**
46
+ DataView 对存储在缓冲内的数据类型没有预设。
47
+
48
+ ECMAScript 6 支持 8 种不同的 ElementType
49
+
50
+ 1 2 4 8
51
+
52
+ Int8 1 8 位有符号整数 signed char 128~127
53
+ Uint8 1 8 位无符号整数 unsigned char 0~255
54
+ Int16 2 16 位有符号整数 short 32 768~32 767
55
+ Uint16 2 16 位无符号整数 unsigned short 0~65 535
56
+ Int32 4 32 位有符号整数 int 2 147 483 648~2 147 483 647
57
+ Uint32 4 32 位无符号整数 unsigned int 0~4 294 967 295
58
+ Float32 4 32 位 IEEE-754 浮点数 float 3.4e+38~+3.4e+38
59
+ Float64 8 64 位 IEEE-754 浮点数 double 1.7e+308~+1.7e+308
60
+
61
+ */
62
+
63
+
64
+ /**
65
+
66
+ DataView 每种类型都暴露了 get 和 set 方法,这些方法使用 byteOffset(字节偏移量)定位要读取或写入值的位置。
67
+
68
+
69
+ 类型是可以互换使用的
70
+
71
+ 声明 ArrayBuffer 则会将所有二进制位初始化为 0
72
+ 。ArrayBuffer 在分配失败时会抛出错误。
73
+
74
+ 通过声明 ArrayBuffer 分配的堆内存可以被当成垃圾回收,不用手动释放。
75
+
76
+ // 说明整个缓冲确实所有二进制位都是 0
77
+ // 检查第一个和第二个字符
78
+ alert(view.getInt8(0)); // 0
79
+ alert(view.getInt8(1)); // 0
80
+
81
+
82
+ // 检查整个缓冲
83
+ alert(view.getInt16(0)); // 0
84
+
85
+ */
86
+
87
+ /**
88
+
89
+
90
+ // 将整个缓冲都设置为 1
91
+ // 255 的二进制表示是 11111111(2^8 - 1)
92
+ view.setUint8(0, 255);
93
+
94
+ // DataView 会自动将数据转换为特定的 ElementType
95
+ // 255 的十六进制表示是 0xFF
96
+ view.setUint8(1, 0xFF);
97
+
98
+
99
+ // 现在,缓冲里都是 1 了
100
+ // 如果把它当成二补数的有符号整数,则应该是-1
101
+ alert(view.getInt16(0)); // -1
102
+
103
+
104
+ 2. 字节序
105
+
106
+ “字节序”指的是计算系统维护的一种字节顺序的约定。DataView 只支持两种约定:大端字节序和小端字节序。大端字节序也称为“网络字节序”,意思是最高有效位保存在第一个字节,而最低有效位保存在最后一个字节。小端字节序正好相反,即最低有效位保存在第一个字节,最高有效位保存在最后一个字节。
107
+
108
+
109
+ JavaScript 运行时所在系统的原生字节序决定了如何读取或写入字节,但 DataView 并不遵守这个约定。
110
+
111
+ 对一段内存而言,DataView 是一个中立接口,它会遵循你指定的字节序。DataView 的所有 API 方法都以大端字节序作为默认值,但接收一个可选的布尔值参数,设置为 true 即可启用小端字节序。
112
+
113
+ // 在内存中分配两个字节并声明一个 DataView
114
+ const buf = new ArrayBuffer(2);
115
+ const view = new DataView(buf);
116
+
117
+ // 填充缓冲,让第一位和最后一位都是 1
118
+ view.setUint8(0, 0x80); // 设置最左边的位等于 1
119
+ view.setUint8(1, 0x01); // 设置最右边的位等于 1
120
+
121
+ // 缓冲内容(为方便阅读,人为加了空格)
122
+ // 0x8 0x0 0x0 0x1
123
+ // 1000 0000 0000 0001
124
+
125
+ // 按大端字节序读取 Uint16
126
+ // 0x80 是高字节,0x01 是低字节
127
+ // 0x8001 = 2^15 + 2^0 = 32768 + 1 = 32769
128
+ alert(view.getUint16(0)); // 32769
129
+
130
+ // 按小端字节序读取 Uint16
131
+ // 0x01 是高字节,0x80 是低字节
132
+ // 0x0180 = 2^8 + 2^7 = 256 + 128 = 384
133
+ alert(view.getUint16(0, true)); // 384
134
+
135
+ // 按大端字节序写入 Uint16
136
+ view.setUint16(0, 0x0004);
137
+
138
+ // 缓冲内容(为方便阅读,人为加了空格)
139
+ // 0x0 0x0 0x0 0x4
140
+ // 0000 0000 0000 0100
141
+
142
+ alert(view.getUint8(0)); // 0
143
+ alert(view.getUint8(1)); // 4
144
+
145
+ // 按小端字节序写入 Uint16
146
+ view.setUint16(0, 0x0002, true);
147
+
148
+ // 缓冲内容(为方便阅读,人为加了空格)
149
+ // 0x0 0x2 0x0 0x0
150
+ // 0000 0010 0000 0000
151
+
152
+ alert(view.getUint8(0)); // 2
153
+ alert(view.getUint8(1)); // 0
154
+
155
+
156
+ 3. 边界情形
157
+
158
+ DataView 完成读、写操作的前提是必须有充足的缓冲区,否则就会抛出 RangeError
159
+
160
+
161
+ const buf = new ArrayBuffer(6);
162
+ const view = new DataView(buf);
163
+
164
+ // 尝试读取部分超出缓冲范围的值
165
+ view.getInt32(4);
166
+ // RangeError
167
+
168
+ // 尝试读取超出缓冲范围的值
169
+ view.getInt32(8);
170
+ // RangeError
171
+
172
+ // 尝试读取超出缓冲范围的值
173
+ view.getInt32(-1);
174
+ // RangeError
175
+
176
+ // 尝试写入超出缓冲范围的值
177
+ view.setInt32(4, 123);
178
+ // RangeError
179
+
180
+ const buf = new ArrayBuffer(1);
181
+ const view = new DataView(buf);
182
+
183
+ view.setInt8(0, 1.5);
184
+ alert(view.getInt8(0)); // 1
185
+
186
+ view.setInt8(0, [4]);
187
+ alert(view.getInt8(0)); // 4
188
+
189
+ view.setInt8(0, 'f');
190
+ alert(view.getInt8(0)); // 0
191
+
192
+ view.setInt8(0, Symbol());
193
+ // TypeError
194
+
195
+
196
+ */
197
+
198
+
199
+ // 0x80
200
+ // 0x是C语言中16进制数的表示方法。
201
+ // 0x80等于十进制的128
202
+
203
+ // 0x80在计算机内部表示为1000 0000
204
+
205
+ /**
206
+ *
207
+ const buf = new ArrayBuffer(2);
208
+ undefined
209
+ const view = new DataView(buf);
210
+ undefined
211
+ view.setUint8(0,0*81)
212
+ undefined
213
+ view.setUint8(1,0*10);
214
+ undefined
215
+ view
216
+ DataView(2)buffer: ArrayBuffer(2) byteLength: 2[[Prototype]]: ArrayBufferbyteLength: 2constructor: ƒ ArrayBuffer()slice: ƒ slice()Symbol(Symbol.toStringTag): "ArrayBuffer"get byteLength: ƒ byteLength()[[Prototype]]: Object[[Int8Array]]: Int8Array(2)0: 01: 0buffer: ArrayBuffer(2) byteLength: 2byteOffset: 0length: 2Symbol(Symbol.toStringTag): "Int8Array"[[Prototype]]: TypedArray[[Uint8Array]]: Uint8Array(2)0: 01: 0buffer: ArrayBuffer(2) byteLength: 2byteOffset: 0length: 2Symbol(Symbol.toStringTag): "Uint8Array"[[Prototype]]: TypedArray[[Int16Array]]: Int16Array(1)0: 0buffer: ArrayBuffer(2) byteLength: 2byteOffset: 0length: 1Symbol(Symbol.toStringTag): "Int16Array"[[Prototype]]: TypedArray[[ArrayBufferByteLength]]: 2[[ArrayBufferData]]: 328byteLength: 2byteOffset: 0[[Prototype]]: DataView
217
+ view.buffer
218
+ ArrayBuffer(2)byteLength: 2[[Prototype]]: ArrayBuffer[[Int8Array]]: Int8Array(2)[[Uint8Array]]: Uint8Array(2)[[Int16Array]]: Int16Array(1)[[ArrayBufferByteLength]]: 2[[ArrayBufferData]]: 328
219
+ view.getUnit16(0);
220
+ VM1065:1 Uncaught TypeError: view.getUnit16 is not a function
221
+ at <anonymous>:1:6
222
+ (匿名) @ VM1065:1
223
+ view.getUint16(0);
224
+ 0
225
+ view.setUint8(0, 0x80);
226
+ undefined
227
+ view.setUint8(1, 0x01);
228
+ undefined
229
+ view.getUint16(0);
230
+ 32769
231
+ view.getUint16(0, true);
232
+ 384
233
+ view.getUint16(0, 0x0004);
234
+ 384
235
+ view.setUint16(0, 0x004);
236
+ undefined
237
+ view.getUint8(0);
238
+ 0
239
+ view.getUint8(1);
240
+ 4
241
+ view.getUint16(0, 0x0040);
242
+ 1024
243
+ view.getUint16(0,0x004);
244
+ 1024
245
+ view.setUint16(0, 0x002);
246
+ undefined
247
+ view.getUint16(0, 0x002);
248
+ 512
249
+ view.getUint8(0);
250
+ 0
251
+ view.getUint8(1);
252
+ 2
253
+ view.setUint16(0, 0x0002, true);
254
+ undefined
255
+ view.getUint8(0);
256
+ 2
257
+ view.getUint8(1);
258
+ 0
259
+ */
0 commit comments