前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库DataView 字节序 与类型化数组区别
JavaScript数据处理

解析二进制协议时,为什么要用 DataView 而不是 Uint16Array?

类型化数组把整块内存看成同一种元素,按本机字节序读,偏移还必须对齐。DataView 可以从任意字节位置读 8、16、32 位字段,每次读都能指定大端还是小端,所以解析协议和文件格式要用它。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数据处理
先看核心答案读代码示例
理解线索

同一块内存,两种读法

  1. ArrayBuffer一段原始字节,本身不能读写,要套一个视图才能用
  2. 类型化数组把字节切成等宽元素,按本机字节序解释,适合像素、音频采样
  3. DataView按字段读,偏移随意,每次调用自己决定字节序

字节序不会改变内存里的字节,只决定读多字节数值时哪个字节算高位。所以同一段字节按大端、小端读出来是两个数,但原始数据一个字节都没动。

核心回答

先记住这个答案

类型化数组(比如 Uint16Array)构造时就定死了元素类型,之后每个下标都按同样的宽度、按本机字节序去读,而且视图的起始偏移必须是元素宽度的整数倍。二进制协议往往是 1 字节类型、2 字节大端长度、4 字节小端 ID 紧挨着排,宽度不一样,字节序也不一样,还不对齐。DataView 正好解决这三件事:任意偏移、任意宽度,每次调用 getUint16(offset, littleEndian) 都能自己指定字节序。

  • DataView 每次读写都能单独指定字节序
  • DataView 默认按大端读,小端要传 true
  • 类型化数组用本机字节序,偏移必须是元素宽度的倍数
  • 同类数值批量计算用类型化数组,混合字段用 DataView

解析一个真实的协议头

假设服务端通过 WebSocket 发来一段二进制消息,协议文档写着:第 0 字节是消息类型,第 1~2 字节是大端的长度,第 3~6 字节是小端的用户 ID。用 DataView 照着文档一行一行读就行。

按字段读取不同宽度和字节序JavaScript
// 1 字节类型 + 2 字节长度(大端)+ 4 字节 ID(小端)
const bytes = Uint8Array.from([0x01, 0x00, 0x10, 0x78, 0x56, 0x34, 0x12])
const view = new DataView(bytes.buffer)

const type = view.getUint8(0) // 单字节,不涉及字节序
const length = view.getUint16(1) // 不传第二个参数就是大端
const id = view.getUint32(3, true) // 传 true 按小端读

console.log(type, length, id.toString(16))
查看输出与解释
1 16 12345678

length 从偏移 1 开始读两个字节 00 10,大端把前一个字节当高位,结果是 16。id 读的是 78 56 34 12,小端把最后一个字节当高位,拼出来就是 0x12345678。偏移 1 和偏移 3 都不是 2 或 4 的倍数,DataView 照样能读。 大端和小端可以这样记:大端是「高位在前」,跟我们写数字的习惯一样;小端是「低位在前」,x86 和绝大多数 ARM 设备的内存就是这么存的。网络协议传统上用大端,所以也叫网络字节序。

类型化数组卡在哪两件事上

类型化数组不是不能读二进制,而是它假设整块内存都是同一种元素,而且是本机写出来的。拿它去读外部来的数据,就会碰到字节序和对齐两个问题。

本机字节序和对齐限制JavaScript
const buf = Uint8Array.from([0x01, 0x02, 0x03, 0x04]).buffer

const u16 = new Uint16Array(buf)
console.log(u16[0].toString(16)) // 按本机字节序读,小端机器上是 0x0201

const view = new DataView(buf)
console.log(view.getUint16(0).toString(16)) // DataView 默认大端,是 0x0102

try {
  new Uint16Array(buf, 1) // 起始偏移 1 不是 2 的倍数
} catch (err) {
  console.log(err.name)
}
查看输出与解释
201
102
RangeError

同样两个字节 01 02,用 Uint16Array 在小端机器上读成 0x0201,而 DataView 默认读成 0x0102。如果协议规定的是大端,用类型化数组就读错了,而且换到大端机器上结果还会变。 第二个问题是对齐。Uint16Array 的起始偏移必须是 2 的倍数,Uint32Array 必须是 4 的倍数,否则构造时直接抛 RangeError。紧凑协议里字段一个挨一个,根本不保证对齐。

什么时候反而该用类型化数组
数据是本机自己产生、自己消费的同质数组时,类型化数组更合适。比如 Canvas 的 ImageData.data 是 Uint8ClampedArray,Web Audio 的采样是 Float32Array,WebGL 的顶点数据也是类型化数组。这些场景要的是批量按下标读写,速度快,字节序也不用管,因为从头到尾都在同一台机器上。
回答前,多想一步

容易答错的地方

以为 DataView 默认是小端
多字节方法的 littleEndian 参数省略时是 false,也就是按大端读。很多人因为电脑是小端机,就以为 DataView 默认小端。协议是小端时一定要显式传 true,别靠记忆。
把字节序理解成字节数组被反转了
换字节序不会动内存里的任何一个字节,变的只是读多字节数值时谁算高位。同一个 ArrayBuffer 可以同时被一个大端读取、一个小端读取,互不影响。
读之前不检查长度
DataView 不认识你的协议,读超出 buffer 范围时直接抛 RangeError。解析网络包要先校验最小长度,再用头部里声明的长度限制后面的偏移,否则截断的包或者恶意伪造的长度字段会让解析崩掉。
试着用自己的话回答

面试官还会怎么问?

读单字节字段需要传字节序参数吗?

不需要。字节序只影响多个字节怎么组合成一个数,getUint8 和 getInt8 只读一个字节,所以它们根本没有 littleEndian 参数。

怎么判断当前机器是大端还是小端?

用同一块内存写一个多字节数再按字节读回来:new Uint8Array(new Uint16Array([1]).buffer)[0] === 1 为 true 就是小端。实际项目里几乎不需要判断,因为用 DataView 显式指定字节序,就和机器无关了。

浮点数和 64 位整数也能指定字节序吗?

可以。DataView 有 getFloat32 和 getFloat64,还有返回 BigInt 的 getBigInt64 和 getBigUint64,对应的 set 方法也一样,第二个或第三个参数就是 littleEndian。

从一道题,走向一组知识

把知识连起来

数据处理

new Date 传数字、传字符串、传多个参数,分别按什么时区算?

字节序是「同样的字节怎么解释」,Date 构造器是「同样的字段按哪个时区解释」,都是解释规则决定结果。

数据处理

夏令时切换那天,用 Date 做时间计算会出什么问题?

两题都在讲同一份原始数据换一种解释方式就会得到不同结果,夏令时是时间版本的这个问题。

参考资料

  • JavaScript:数据处理参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 解析一个真实的协议头
  3. 类型化数组卡在哪两件事上
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑