JavaScript Definitive Guide-Chapter 11 Notes QA

1、 理解 TypedArray 与 ArrayBuffer:共享内存机制详解

📘 示例代码

let ints = new Int16Array([0,1,2,3,4,5,6,7,8,9])
let last3 = ints.subarray(ints.length-3, ints.length)
ints
// Int16Array(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

last3
// Int16Array(3) [7, 8, 9]

ints[9] = -1
last3
// Int16Array(3) [7, 8, -1]

last3.buffer
// ArrayBuffer { [Uint8Contents]: <00 00 01 00 02 00 ... ff ff>, byteLength: 20 }

last3.buffer === ints.buffer
// true

last3.byteOffset
// 14

last3.byteLength
// 6

last3.buffer.byteLength
// 20

🧩 一、创建一个 16 位整数数组

let ints = new Int16Array([0,1,2,3,4,5,6,7,8,9])
  • 每个元素占 2 字节(Int16 = 16 位)。
  • 共 10 个数 → 总字节数 = 10 × 2 = 20 字节
  • 底层的 20 字节数据存在一个叫 ArrayBuffer 的连续内存中。

ints 只是对这块内存的一个“视图”。


🧠 二、从中“切”出一段子视图

let last3 = ints.subarray(ints.length - 3, ints.length)

表示从原数组第 7、8、9 个元素开始,
创建一个新视图 last3,对应 [7, 8, 9]

⚠️ subarray() 不会复制数据,只是创建一个“窗口”,
这个窗口仍然指向同一块底层内存。


🔄 三、共享内存效果

ints[9] = -1

修改原数组的最后一个元素后:

last3  // Int16Array(3) [ 7, 8, -1 ]

last3 自动变化,因为它与 ints 共享同一块内存


🧱 四、底层内存结构(ArrayBuffer)

last3.buffer === ints.buffer  // true

说明两者共享同一个底层 ArrayBuffer

属性 含义
last3.byteOffset 从原内存起始位置偏移的字节数 14
last3.byteLength 当前视图覆盖的字节长度 6
ints.buffer.byteLength 整个底层内存大小 20

✅ 五、总结一句话

概念 通俗解释
ArrayBuffer 一整块原始二进制内存
TypedArray(如 Int16Array) 看这块内存的“窗口”
subarray() 不复制数据,只截取一段“视图”
共享内存效果 改一个,另一个也会变

💡 类比理解

  • ArrayBuffer 像是一整张 A4 纸
  • Int16Array 是你拿尺子量出来的 数据区域
  • subarray() 就是用透明片框出“纸上某一区域”
  • 改动纸上内容,所有透明片看到的部分都会变

2、 小端序(Little Endian)与大端序(Big Endian)解析

非常好的问题 👍

“小端序(Little Endian)”和“大端序(Big Endian)”其实就是在多字节数据里,计算机存储字节顺序的两种方式。

🧠 一句话解释

术语 含义
大端序(Big Endian) 高位字节放在前面(低地址)
小端序(Little Endian) 低位字节放在前面(低地址)

📘 举个最通俗的例子

假设有一个 32 位整数:

0x12345678

它占 4 个字节(每字节两个十六进制位):

12 34 56 78
存储方式 低地址 → 高地址的排列顺序 形象记忆
大端序(Big Endian) 12 34 56 78 像人类写数字,从左到右
小端序(Little Endian) 78 56 34 12 把“数字反着写”存进去

💡 比喻记忆法

  • 大端序:把“大头”(高位)放前面,就像我们写数字“1234”,从高位写起。
  • 小端序:把“小头”(低位)放前面,就像把数字“1234”倒着放成“4321”。

🧩 举个真实例子(JavaScript + DataView)

let buffer = new ArrayBuffer(4);
let view = new DataView(buffer);

// 小端序
view.setUint32(0, 0x12345678, true); // true 表示小端序
console.log(new Uint8Array(buffer)); // 输出: [120, 86, 52, 18] = 0x78 0x56 0x34 0x12

// 大端序
view.setUint32(0, 0x12345678, false);
console.log(new Uint8Array(buffer)); // 输出: [18, 52, 86, 120] = 0x12 0x34 0x56 0x78

✅ 总结一句话

端序 存储规则 直观印象
大端序 (Big Endian) 高位在前,低位在后 像人类读写数字
小端序 (Little Endian) 低位在前,高位在后 计算机常用方式(如 Intel CPU)

🧭 所以说:

“端序”只是决定多字节数在内存中是正着放还是反着放,数据本身的数值是一模一样的,只是存放顺序不同而已。

32 位整数为什么占 4 个字节

非常好,这正是“位(bit)”和“字节(byte)”容易混淆的地方 👍

🧠 一、基本换算

1 字节(Byte) = 8 位(bit)

  • 一个“位(bit)”就是一个二进制数字(0 或 1)
  • 一个“字节(Byte)”就是 8 个二进制位

💡 二、32 位整数是啥意思?

“32 位”整数指的是这个整数用 32 个二进制位来存储。

计算它占多少字节:

32 位 ÷ 8 位/字节 = 4 字节

📘 三、举例:0x12345678

  • 每个十六进制数字(0–F)正好对应 4 位二进制
  • 共有 8 个十六进制数字 → 8 × 4 = 32 位 = 4 字节
表示方式 位数 字节数
0x12 8 位 1 字节
0x1234 16 位 2 字节
0x12345678 32 位 4 字节

🎯 四、形象比喻

想象你要装 32 个小球(bit),每个盒子(byte)能装 8 个小球,你就需要:

32 ÷ 8 = 4 个盒子

✅ 一句话总结:

“32 位整数”表示它占用 32 个二进制位(bit)来存储数据,而每 8 位是 1 字节(Byte),所以总共占 4 个字节(4 Bytes)。

为什么一个十六进制数字对应 4 个二进制位

🧠 一、二进制表示范围

二进制位数 能表示的范围 数量
1 位 0~1 2 种
2 位 00~11 4 种
3 位 000~111 8 种
4 位 0000~1111 16 种

💡 二、十六进制就是“16 进制”

十六进制(Hexadecimal)有 16 个符号:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
十六进制 对应二进制(4位)
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
A 1010
B 1011
C 1100
D 1101
E 1110
F 1111

📘 三、举例:0x12345678

  • 8 个十六进制数字 × 4 位二进制 = 32 位 = 4 字节

✅ 总结:

因为 4 位二进制正好能表示 16 种情况(0~15),而十六进制正好有 16 个符号(0~F),所以 1 个十六进制数 = 4 个二进制位。

3、JavaScript 判断系统小端序示例解析

代码原文

let littleEndian = new Int8Array(new Int32Array([1]).buffer)[0] === 1;

逐步解析

  1. new Int32Array([1])

    • 创建一个长度为 1 的 32 位整数数组
    • 数组内容是 [1]
    • 在内存中,这个整数 1 会占 4 个字节(32 位)。
  2. .buffer

    • Int32Array 是一个类型化数组(TypedArray)。
    • .buffer 属性返回其 底层的 ArrayBuffer 内存对象
    • ArrayBuffer 可以用其他类型的视图来读取同一块内存。
  3. new Int8Array(...).buffer

    • 将上面的 32 位整数的 buffer 包装成 8 位整数视图(Int8Array)
    • 8 位数组可以按字节读取内存里的内容。
    • 因为 Int32Array 占 4 个字节,Int8Array 也会有 4 个元素。
  4. [0]

    • 读取 8 位数组的第 0 个元素,也就是 内存中第一个字节的值
  5. === 1

    • 判断第一个字节是否为 1
    • 如果 第一个字节是最低位(低地址存低位),说明系统是 小端序(Little Endian)
    • 如果第一个字节是 0,说明系统是 大端序(Big Endian)

总结一句话

let littleEndian = new Int8Array(new Int32Array([1]).buffer)[0] === 1;

意思就是:

通过检查 32 位整数 1 的第一个字节,判断当前系统是否采用小端序存储。

  • true → 小端序
  • false → 大端序

💡 形象记忆

  • 小端序:低位(小头)先放 → 第一个字节 = 1
  • 大端序:高位(大头)先放 → 第一个字节 = 0

4、JavaScript DataView 示例解析

代码原文

let bytes = new Uint8Array(1024);
let ints = new Uint32Array(bytes.buffer);
let floats = new Float64Array(bytes.buffer);
let view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);

let int = view.getInt32(0);
int;
int = view.getInt32(4, false);
int;
int = view.getInt32(8, true);
view.setUint32(8, int, false);

逐步解析

1️⃣ 创建基础内存缓冲区

let bytes = new Uint8Array(1024);
  • 创建一个长度为 1024 的 8 位无符号整数数组
  • 实际上就是申请了 1024 个字节的内存。

2️⃣ 创建其他类型数组视图

let ints = new Uint32Array(bytes.buffer);
let floats = new Float64Array(bytes.buffer);
  • bytes.buffer 是底层的 ArrayBuffer(一块连续的内存)。

  • 可以用不同的类型视图(TypedArray)来读取同一块内存:

    • Uint32Array:每 4 个字节组成一个 32 位无符号整数
    • Float64Array:每 8 个字节组成一个 64 位浮点数
  • 这样可以在不复制内存的情况下,用不同类型读取或写入数据。

3️⃣ DataView 的作用

let view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
  • DataView 是一个 通用视图,可以在同一块内存上按任意偏移读取/写入多种类型数据

  • 与 TypedArray 不同的是:

    • 可以指定字节偏移(offset)
    • 可以灵活选择大端序或小端序(endianness)
  • 常用于处理二进制协议或网络数据。

4️⃣ 使用 DataView 读取和写入数据

let int = view.getInt32(0);
  • getInt32(offset):从指定偏移读取 32 位有符号整数。
  • 如果没指定小端序,默认使用大端序
int = view.getInt32(4, false);
  • 第二个参数 false 表示使用大端序(Big Endian)
int = view.getInt32(8, true);
  • 第二个参数 true 表示使用小端序(Little Endian)
view.setUint32(8, int, false);
  • setUint32(offset, value, littleEndian):在指定偏移写入 32 位无符号整数。
  • 第三个参数决定使用小端还是大端存储

5️⃣ 总结

  • ArrayBuffer:内存本身

  • TypedArray(如 Uint8Array、Uint32Array):固定类型视图,按类型顺序访问内存

  • DataView:灵活视图,可按任意偏移读取/写入多种类型,并可控制端序

  • 示例代码最终作用:

    1. 分配 1024 字节内存
    2. 用不同类型视图访问同一块内存
    3. 使用 DataView 精确读取/写入整数,控制端序

💡 小技巧:DataView 常用于解析二进制文件、网络协议或底层数据操作

5、正则表达式中的四种查找断言(Lookaround)通俗讲解


🧠 一句话概念

查找断言(Lookaround)就是一种“只看不吃”的规则:
它只检查前后是否满足条件,不真正匹配内容。


✅ 四种断言方式总览

类型 英文名 语法 含义 方向
正向肯定查找 Positive Lookahead (?=...) 后面必须满足条件 向前(右看)
正向否定查找 Negative Lookahead (?!...) 后面不能满足条件 向前(右看)
反向肯定查找 Positive Lookbehind (?<=...) 前面必须满足条件 向后(左看)
反向否定查找 Negative Lookbehind (?<!...) 前面不能满足条件 向后(左看)

🟢 1. 正向肯定查找 (?=...)

👉 意思:匹配某内容,要求它后面必须是……

let text = "100px 200em";
console.log(text.match(/\d+(?=px)/g)); // ['100']

解释:
匹配数字,但只有当后面紧跟 px 时才算成功。
200em 不符合。


🔴 2. 正向否定查找 (?!...)

👉 意思:匹配某内容,要求它后面不能是……

let text = "100px 200em";
console.log(text.match(/\d+(?!px)/g)); // ['200']

解释:
匹配数字,但后面 不是 px 的才算数。


🟢 3. 反向肯定查找 (?<=...)

👉 意思:匹配某内容,要求它前面必须是……

let text = "$100 €200";
console.log(text.match(/(?<=\$)\d+/g)); // ['100']

解释:
匹配数字,但只有当左边是 $ 时才匹配成功。


🔴 4. 反向否定查找 (?<!...)

👉 意思:匹配某内容,要求它前面不能是……

let text = "$100 €200";
console.log(text.match(/(?<!\$)\d+/g)); // ['200']

解释:
匹配数字,但前面 不是 $ 的才算。


🧭 一张图理解方向关系

     ←—— 向后看 ——┐
                   │
[A][B][C][D][E][F]
       │
       └—— 向前看 ——→

✅ 一句话总结

类型 检查方向 判断逻辑 示例效果
(?=...) 向右看 后面必须满足 数字后面是 px
(?!...) 向右看 后面不能满足 数字后面不是 px
(?<=...) 向左看 前面必须满足 前面是 $
(?<!...) 向左看 前面不能满足 前面不是 $

📘 记忆口诀:

✅ 肯定查找:要有;❌ 否定查找:不要有;
👀 向前查:看右边;👈 向后查:看左边。

6、JavaScript 正则表达式标志(Flags)详解


🧠 一句话概览

正则表达式的标志(flags)是写在表达式后面的修饰符,用来控制匹配方式
常见 6 个标志:g, i, m, s, u, y


✅ 1. g —— 全局匹配(Global)

含义:

匹配字符串中所有符合条件的结果,而不是遇到第一个就停。

示例:

let text = "cat, cat, cat";
console.log(text.match(/cat/g)); // ['cat', 'cat', 'cat']

📘 没有 g

console.log(text.match(/cat/)); // ['cat', index: 0, input: 'cat, cat, cat']

👉 只返回第一个匹配结果。


✅ 2. i —— 忽略大小写(Ignore Case)

含义:

匹配时不区分大小写

示例:

let text = "Hello hello HELLO";
console.log(text.match(/hello/gi)); // ['Hello', 'hello', 'HELLO']

📘 没有 i

console.log(text.match(/hello/g)); // ['hello']

✅ 3. m —— 多行匹配(Multiline)

含义:

^$ 可以匹配每一行的开头与结尾,而不仅仅是整个字符串的开头与结尾。

示例:

let text = "apple\nbanana\ncherry";
console.log(text.match(/^b\w+/gm)); // ['banana']

📘 没有 m

console.log(text.match(/^b\w+/g)); // null

✅ 4. s —— 单行模式(dotAll)

含义:

让点号 . 可以匹配换行符 \n
默认情况下,. 不能匹配换行符

示例:

let text = "hello\nworld";
console.log(text.match(/hello.world/s)); // ['hello\nworld']

📘 没有 s

console.log(text.match(/hello.world/)); // null

✅ 5. u —— Unicode 模式(Unicode)

含义:

启用对 Unicode 字符的完整支持,让正则可以识别如 emoji、汉字等。

示例:

let text = "😊a";
console.log(text.match(/\p{Emoji}/u)); // ['😊']

📘 没有 u

console.log(text.match(/\p{Emoji}/)); // 报错:Invalid escape

✅ 6. y —— 粘连匹配(Sticky)

含义:

匹配时从上一次匹配结束的位置继续开始
不会跳过中间的字符(区别于 g)。

示例:

let text = "aaa_aaa_aaa";
let regex = /a+/y;

regex.lastIndex = 0;
console.log(regex.exec(text)); // ['aaa']

regex.lastIndex = 3;
console.log(regex.exec(text)); // null (因为中间有 '_' 断开)

📘 如果用 g

let regex2 = /a+/g;
console.log(text.match(regex2)); // ['aaa', 'aaa', 'aaa']

🧭 总结对比表

标志 英文名 含义 示例效果
g global 匹配所有结果 多次匹配
i ignoreCase 忽略大小写 Aa
m multiline 多行匹配 ^ $ 匹配行首行尾
s dotAll . 匹配换行符 允许跨行匹配
u unicode 支持 Unicode 支持 emoji、汉字等
y sticky 粘连匹配 从上次结束处继续

📘 一句话记忆口诀

g 全局找,
i 不分大小写,
m 按行看,
s 跨行点,
u 懂 Unicode,
y 粘着走不跳。

7、 📊 JavaScript console.table() 函数详解与示例


🧠 一句话概念

console.table() 用来把数据以 表格形式 输出到控制台(Console),
console.log() 更直观、可读性更强。


📘 基本语法

console.table(data, columns);
参数名 类型 说明
data 数组 或 对象 要显示的内容
columns 数组(可选) 指定要显示的列(字段名)

🧩 示例 1:数组对象

let users = [
  { name: "Alice", age: 25, city: "Beijing" },
  { name: "Bob", age: 30, city: "Shanghai" },
  { name: "Cindy", age: 22, city: "Guangzhou" }
];

console.table(users);

输出效果(在浏览器控制台中):

(index) name age city
0 Alice 25 Beijing
1 Bob 30 Shanghai
2 Cindy 22 Guangzhou

🧩 示例 2:只显示部分列

console.table(users, ["name", "city"]);

输出:

(index) name city
0 Alice Beijing
1 Bob Shanghai
2 Cindy Guangzhou

🧩 示例 3:对象(键值对)

let scores = {
  Alice: 95,
  Bob: 88,
  Cindy: 91
};

console.table(scores);

输出:

(index) Value
Alice 95
Bob 88
Cindy 91

🧩 示例 4:嵌套对象数组

let products = [
  { id: 1, name: "Laptop", price: 5999 },
  { id: 2, name: "Phone", price: 3999 },
  { id: 3, name: "Tablet", price: 2999 }
];

console.table(products);

输出效果与表格类似,更方便阅读调试。


✅ 小结

功能 说明
✅ 输出表格形式 更直观地展示数组或对象内容
✅ 可选显示字段 可通过第二个参数过滤字段
✅ 调试利器 调试时比 console.log() 更高效

💡 适用于调试数组、对象、API 返回结果、数据结构等场景。

posted @ 2025-11-02 08:08  AlphaGeek  阅读(0)  评论(0)    收藏  举报