
2025-04-06 日报 Day148

今日的鸡汤
少欲则心静,心静则事简,只有这样方能够挣脱繁华虚妄的羁绊,拥抱明确而简单的生活。
今日学习内容
1、JS 红皮书 P616-626 第二十章:JavaScript API
今日笔记
1、 跨上下文消息: 跨文档消息,有时候也简称为 XDM(cross-document messaging),是一种在不同执行上下文(如不同工作线程或不同源的页面)间传递信息的能力。
注意 跨上下文消息用于窗口之间通信或工作线程之间通信。本节主要介绍使用postMessage()与其他窗口通信 。关于工作线程之间通信、MessageChannel 和BroadcastChannel,可以参考第 27 章。
postMessage()方法接收 3 个参数:消息、表示目标接收源的字符串和可选的可传输对象的数组(只与工作线程相关)。第二个参数对于安全非常重要,其可以限制浏览器交付数据的目标。下面来看一个例子:
1 | let iframeWindow = document.getElementById("myframe").contentWindow; |
接收到 XDM 消息后,window 对象上会触发 message 事件。这个事件是异步触发的,因此从消息发出到接收到消息(接收窗口触发 message 事件)可能有延迟。传给 onmessage 事件处理程序的 event对象包含以下 3 方面重要信息。
data:作为第一个参数传递给 postMessage()的字符串数据。
origin:发送消息的文档源,例如”http://www.wrox.com"。
source:发送消息的文档中 window 对象的代理。这个代理对象主要用于在发送上一条消息的窗口中执行 postMessage()方法。如果发送窗口有相同的源,那么这个对象应该就是 window对象。
接收消息之后验证发送窗口的源是非常重要的。与 postMessage()的第二个参数可以保证数据不会意外传给未知页面一样,在 onmessage 事件处理程序中检查发送窗口的源可以保证数据来自正确的地方。基本的使用方式如下所示:
1 | window.addEventListener("message", (event) => { |
大多数情况下,event.source 是某个 window 对象的代理,而非实际的 window 对象。因此不能通过它访问所有窗口下的信息。最好只使用 postMessage(),这个方法永远存在而且可以调用。
XDM 有一些怪异之处。首先,postMessage()的第一个参数的最初实现始终是一个字符串。后来,第一个参数改为允许任何结构的数据传入,不过并非所有浏览器都实现了这个改变。为此,最好就是只通 过 postMessage() 发送字符串。如果需要传递结构化数据,那么最好先对该数据调用JSON.stringify(),通过 postMessage()传过去之后,再在 onmessage 事件处理程序中调用JSON.parse()。
在通过内嵌窗格加载不同域时,使用 XDM 是非常方便的。这种方法在混搭(mashup)和社交应用中非常常用。通过使用 XDM 与内嵌窗格中的网页通信,可以保证包含页面的安全。XDM 也可以用于同源页面之间通信。
2、Encoding API: Encoding API 主要用于实现字符串与定型数组之间的转换。规范新增了 4 个用于执行转换的全局类:TextEncoder、TextEncoderStream、TextDecoder 和 TextDecoderStream。
注意 相比于批量(bulk)的编解码,对流(stream)编解码的支持很有限。
3、文本编码: Encoding API 提供了两种将字符串转换为定型数组二进制格式的方法:批量编码和流编码。把字符串转换为定型数组时,编码器始终使用 UTF-8。
- 批量编码: 所谓批量,指的是 JavaScript 引擎会同步编码整个字符串。对于非常长的字符串,可能会花较长时间。批量编码是通过 TextEncoder 的实例完成的:这个实例上有一个 encode()方法,该方法接收一个字符串参数,并以 Uint8Array 格式返回每个字符的 UTF-8 编码:
1
const textEncoder = new TextEncoder();
编码器是用于处理字符的,有些字符(如表情符号)在最终返回的数组中可能会占多个索引:1
2
3
4
5
6const textEncoder = new TextEncoder();
const decodedText = 'foo';
const encodedText = textEncoder.encode(decodedText);
// f 的 UTF-8 编码是 0x66(即十进制 102)
// o 的 UTF-8 编码是 0x6F(即二进制 111)
console.log(encodedText); // Uint8Array(3) [102, 111, 111]1
2
3
4
5const textEncoder = new TextEncoder();
const decodedText = '☺';
const encodedText = textEncoder.encode(decodedText);
// ☺的 UTF-8 编码是 0xF0 0x9F 0x98 0x8A(即十进制 240、159、152、138)
console.log(encodedText); // Uint8Array(4) [240, 159, 152, 138] - 流编码: TextEncoderStream 其实就是 TransformStream 形式的 TextEncoder。将解码后的文本流通过管道输入流编码器会得到编码后文本块的流:
4、文本解码: Encoding API 提供了两种将定型数组转换为字符串的方式:批量解码和流解码。与编码器类不同,在将定型数组转换为字符串时,解码器支持非常多的字符串编码,可以参考 Encoding Standard 规范的“Names and labels”一节。
默认字符编码格式是 UTF-8。 - 批量解码: 批量,指的是 JavaScript 引擎会同步解码整个字符串。对于非常长的字符串,可能会花较长时间。批量解码是通过 TextDecoder 的实例完成的:这个实例上有一个 decode()方法,该方法接收一个定型数组参数,返回解码后的字符串:
1
const textDecoder = new TextDecoder();
1
2
3
4
5
6const textDecoder = new TextDecoder();
// f 的 UTF-8 编码是 0x66(即十进制 102)
// o 的 UTF-8 编码是 0x6F(即二进制 111)
const encodedText = Uint8Array.of(102, 111, 111);
const decodedText = textDecoder.decode(encodedText);
console.log(decodedText); // foo - 流解码: TextDecoderStream 其实就是 TransformStream 形式的 TextDecoder。将编码后的文本流通过管道输入流解码器会得到解码后文本块的流
5、File API与Blob API: Web 应用程序的一个主要的痛点是无法操作用户计算机上的文件。File API 与 Blob API 是为了让 Web 开发者能以安全的方式访问客户端机器上的文件,从而更好地与这些文件交互而设计的。 - File 类型: File API 仍然以表单中的文件输入字段为基础,但是增加了直接访问文件信息的能力。HTML5 在DOM 上为文件输入元素添加了 files 集合。当用户在文件字段中选择一个或多个文件时,这个 files集合中会包含一组 File 对象,表示被选中的文件。每个 File 对象都有一些只读属性。
name:本地系统中的文件名。
size:以字节计的文件大小。
type:包含文件 MIME 类型的字符串。
lastModifiedDate:表示文件最后修改时间的字符串。这个属性只有 Chome 实现了。1
2
3
4
5
6
7
8
9
10
11let filesList = document.getElementById("files-list");
filesList.addEventListener("change", (event) => {
let files = event.target.files,
i = 0,
len = files.length;
while (i < len) {
const f = files[i];
console.log(`${f.name} (${f.type}, ${f.size} bytes)`);
i++;
}
}); - FileReader类型: FileReader类型表示一种异步文件读取机制。可以把FileReader 想象成类似于XMLHttpRequest,只不过是用于从文件系统读取文件,而不是从服务器读取数据。FileReader 类型提供了几个读取文件数据的方法。
readAsText(file, encoding):从文件中读取纯文本内容并保存在 result 属性中。第二个参数表示编码,是可选的。
readAsDataURL(file):读取文件并将内容的数据 URI 保存在 result 属性中。
readAsBinaryString(file):读取文件并将每个字符的二进制数据保存在 result 属性中。
readAsArrayBuffer(file):读取文件并将文件内容以 ArrayBuffer 形式保存在 result 属性。 - FileReaderSync类型: FileReaderSync 类型就是 FileReader 的同步版本。这个类型拥有与 FileReader相同的方法,只有在整个文件都加载到内存之后才会继续执行。FileReaderSync 只在工作线程中可用,因为如果读取整个文件耗时太长则会影响全局。
假设通过 postMessage()向工作线程发送了一个 File 对象。以下代码会让工作线程同步将文件读取到内存中,然后将文件的数据 URL 发回来:1
2
3
4
5
6
7
8
9
10
11// worker.js
self.omessage = (messageEvent) => {
const syncReader = new FileReaderSync();
console.log(syncReader); // FileReaderSync {}
// 读取文件时阻塞工作线程
const result = syncReader.readAsDataUrl(messageEvent.data);
// PDF 文件的示例响应
console.log(result); // data:application/pdf;base64,JVBERi0xLjQK...
// 把 URL 发回去
self.postMessage(result);
}; - Blob与部分读取: File 对象提供了一个名为 slice()的方法。slice()方法接收两个参数:起始字节和要读取的字节数。这个方法返回一个 Blob 的实例,而 Blob 实际上是 File 的超类。blob 表示二进制大对象(binary larget object),是 JavaScript 对不可修改二进制数据的封装类型。包含字符串的数组、ArrayBuffers、ArrayBufferViews,甚至其他 Blob 都可以用来创建 blob。Blob构造函数可以接收一个 options 参数,并在其中指定 MIME 类型:
1 | console.log(new Blob(['foo'])); |
Blob 对象有一个 size 属性和一个 type 属性,还有一个 slice()方法用于进一步切分数据。另外也可以使用 FileReader 从 Blob 中读取数据。下面的例子只会读取文件的前 32 字节:
1 | let filesList = document.getElementById("files-list"); |
- 对象URL与Blob: 对象 URL 有时候也称作 Blob URL,是指引用存储在 File 或 Blob 中数据的 URL。对象 URL 的优点是不用把文件内容读取到 JavaScript 也可以使用文件。只要在适当位置提供对象 URL 即可。要创建对象 URL,可以使用 window.URL.createObjectURL()方法并传入 File 或 Blob 对象。这个函数返回的值是一个指向内存中地址的字符串。因为这个字符串是 URL,所以可以在 DOM 中直接使用。例如,以下代码使用对象 URL 在页面中显示了一张图片:使用完数据之后,最好能释放与之关联的内存。只要对象 URL 在使用中,就不能释放内存。如果想表明不再使用某个对象 URL,则可以把它传给 window.URL.revokeObjectURL()。页面卸载时,所有对象 URL 占用的内存都会被释放。不过,最好在不使用时就立即释放内存,以便尽可能保持页面占用最少资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18let filesList = document.getElementById("files-list");
filesList.addEventListener("change", (event) => {
let info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = event.target.files,
reader = new FileReader(),
url = window.URL.createObjectURL(files[0]);
if (url) {
if (/image/.test(files[0].type)) {
output.innerHTML = `<img src="${url}">`;
} else {
output.innerHTML = "Not an image.";
}
} else {
output.innerHTML = "Your browser doesn't support object URLs.";
}
}); - 读取拖放文件: 组合使用 HTML5 拖放 API 与 File API 可以创建读取文件信息的有趣功能。在页面上创建放置目标后,可以从桌面上把文件拖动并放到放置目标。这样会像拖放图片或链接一样触发 drop 事件。被放置的文件可以通过事件的 event.dataTransfer.files 属性读到,这个属性保存着一组 File 对象,就像文本输入字段一样。