这章介绍的API是被多个浏览器支持且其他章节没有提到的
20.1 Atomics 和 SharedArrayBuffer
多个上下文访问SharedArrayBuffer时,如果同时对缓冲区执行操作,可能会出现资源争用
Atomics API的设计初衷时在最少但是稳定的原子行为基础之上,构建复杂的多线程JavaScript程序
20.1.1 SharedArrayBuffer
sharedArrayBuffer 和ArrayBuffer 具有相同的API
ArrayBuffer必须在不同的上下文之间切换,但是SharedArrayBuffer可以被任意多个上下文同时使用
20.1.2 原子操作基础
任何全局上下文中都有Atomics对象,这个对象暴露了用于执行线程安全操作的一系列静态方法,这些方法的第一个参数一般都是TypeArray(SharedArrayBuffer)的引用
1.算数以及位操作方法
// 创建大小为1的缓冲区
let sharedArrayBuffer = new SharedArrayBuffer(1)
// 基于缓冲区创建Unit8Array
let typeArray = new Uint8Array(sharedArrayBuffer)
// 所有ArrayBuffer 全部初始化为0
console.log(typeArray)// Uint8Array[0]
const index = 0;
const increment = 5;
// 对0处的位置+5
Atomics.add(typeArray,index,increment)
console.log(typeArray)// Uint8Array[5]
// 对0处的位置-5
Atomics.sub(typeArray,index,increment)
console.log(typeArray)// Uint8Array[0]
or 或
and 与
xor 异或
2.原子的读和写
所有原子指令相互之间的顺序永远不会重排
使用原子读或原子写保证所有指令都不会相对原子读/写重新排序
Atomics.load() 和 Atomics.store()还可以构建代码围栏,保证非原子指令相对于load() 或 store() 本地重排
3.原子交换
使用exchange() 和 compareExchange()
4.原子Futex操作和加锁
Atomics API 提供了模仿Linux Futex 快速用户控件互斥量的方法,可以作为复杂锁组件的基本组件
Atomics.wait() 和 Atomics.notify()
20.2 跨上下文消息
跨文档消息(XDM) 是一种在不同执行上下文减传递信息的能力
XDM的核心是postMessage()
接受三个参数: 消息,目标接受源的字符串,可选的传输对象的组织
let iframeWindow = document.getElementById('myFrame').contentWindow
iframeWindow.postMessage("A MSG","http://www.wrox.com")
接到XDM消息后,window对象会触发message事件
message事件上的event对象包含三个重要信息:
data: 消息
origin: 发送消息的原文档
source: 发送消息的文档中window对象的代理
20.3 Encoding API
Encoding API主要实现字符串与定型数组之间的转换
20.3.1 文本编码
Encoding API通过批量编码和流编码把字符串转换位定型数组,编码器始终使用UTF-8
1.批量编码
JS会同步编码整个字符串
通过TextEncoder的实例完成:
const textEncoder = new TextEncoder()
const decodeText = 'foo'
const encodeText = textEncoder.encode(decodeText)
console.log(encodeText);
2.流编码
TextEncoderStream就是TransformStream形式的TextEncoder
20.3.2 文本解码
Encoding API通过批量解码和流解码把定型数组转换为字符串
编码器默认使用UTF-8
1.批量解码
通过TextDecoder实例完成
const textDecoder = new TextDecoder()
const encodedText = Uint8Array.of(102, 111, 111)
const decodedText = textDecoder.decode(encodedText)
console.log(decodedText) // foo
2.流解码
TextDecoderStream就是TransformStream形式的TexDecoder
文本解码器流经常和fetch()一起使用,因为响应体可以通作为ReadableStream来处理
20.4 File API 和 Blob API
20.4.1 File
File API以表单中的文件输入字段为基础,但是增加了直接访问文件信息的能力
H5在DOM上为文件输入元素添加了files集合
files集合会包含一组File对象,每个File对象都只有一些只读属性
name: 本地系统中的文件名
size: 文件大小
type: MIME类型的字符串
lastModifiedDate: 文件最后修改时间
20.4.2 FileReader
readAsText(file,encoding) 读取文件纯文本
readAsDataURL(file) 读取URI
readAsBinaryString(file) 读取二进制数据
readAsArrayBuffer(file) 读取为ArrayBuffer
20.4.3 FileReaderSync
FileReaderSync就是FileReader的同步版本
FileReaderSync只在工作线程中可用
20.4.4 Blob与部分读取
slice()方法读取部分文件,返回一个Blob的实例,而Blob实际上是File的超类
blob表示二进制大对象
20.4.5 对象URL和Blob
对象URL是用来引用储存在File或Blob中数据的URL
对象URL不需要把对象读取到JS也可以使用文件
使用window.URL.createObjectURL(),传入File或Blob,返回一个指向内存中地址的字符串
20.4.6 读取拖放文件
组合使用H5的 拖放API 和 File API,可用从桌面上把文件拖动并放置到相应位置
这样做会触发drop事件
20.5 媒体元素
<video>嵌入视频
<audio>嵌入音频
20.5.1 属性
autoplay 自动播放
loop 循环播放
... p628
20.5.2 事件
abort 下载中断
pause 暂停
play 播放
... p629
20.5.3 自定义媒体播放器
实现简单的媒体播放器
<body>
<div class="mediaplayer">
<div class="video">
<video id="player" src="movie.mov" poster="mymovie.jpg" width="300" height="200">
Video player no available
</video>
</div>
<div class="controls">
<input type="button" value="Play" id="video-btn">
<span id="curtime">0</span> <span id="duration">0</span>
</div>
</div>
<script>
let player = document.getElementById('player'),
btn = document.getElementById('video-btn'),
curtime = document.getElementById('curtime'),
duration = document.getElementById('duration')
duration.innerHTML = player.duration
btn.addEventListener("click",(e)=>{
if(player.paused){
player.play()
btn.value = "Pause"
} else {
player.pause()
btn.value = "Play"
}
})
setInterval(()=>{
curtime.innerHTML = player.currentTime
},250)
</script>
</body>
20.5.4 检测解编码器
在视频元素上使用canPlaytype()检测视频格式
20.5.4 音频类型
要使用Audio播放音频,只需要创建一个Audio的实例并传入音频源文件
创建实例的时候会开始下载,下载完后可用通过play()播放
20.6 原生拖放
现在几乎所有元素都可以拖放
20.6.1 拖放事件
在拖放时,会按顺序触发下面的事件:
dragstart
drag
dragend
把元素拖动到有效的防止目标上时,会依次触发以下事件:
dragenter
dragover
dragleave或drop
20.6.2 自定义放置目标
覆盖dragover和dragenter事件,可用吧任何元素都转化为有效的放置目标
let droptarget = document.getElementById("droptarget")
droptarget.addEventListener("dragover",(e)=>{
e.preventDefault()
})
droptarget.addEventListener("dragenter",(e)=>{
e.preventDefault()
})
20.6.3 dataTransfer对象
事件处理对象event上暴露了dataTransfer对象,用来从被拖动元素向防止目标传递字符串数据
dataTransfer对象有两个方法: getData() 和 setData()
20.6.4 dropEffect 和 effectAllowed
dataTransfer对象不仅可以实现简单的数据传输,还可以用于确定能够对被拖动元素和放置目标执行什么操作
可以使用 dropEffect 和 effectAllowed
dropEffect可以告诉浏览器允许那种放置行为:
none: 不能放到这里
move: 应该移动到放置目标
copy: 应该复制到放置目标
link: 放置目标会导航到被拖动元素
除非同时设置effectAllowed,否则dropEffect属性也没有用
20.6.5 可拖动能力
H5在所有HTML元素上设置了draggable属性,表示元素是否可以拖动
20.6.6 其他成员
H5还为dataTransfer对象定义了如下方法:
addElement(element): 为拖动操作添加元素
clearData(format): 清除以特定格式存储的数据
setDragImage(element,x,y): 允许指定拖动发生时显示在光标下面的图片
20.7 Notifications API
Notifications API用于向用户显示通知,类似alert()的对话框
20.7.1 通知权限
Notifications API默认会开启亮相安全措施:
通知只能在运行安全上下文代码时被触发
通知必须按照每个源的原则明确得到用户允许
20.7.2 显示和隐藏通知
Notification构造函数用于创建和显示通知
可以通过options参数对通知自定义
new Notification("Title text",{
body: 'body text',
image: 'url',
vibrate: true
})
20.7.3 通知生命周期回调
Notification提供了四个用于添加回调的生命周期方法
onshow 在通知显示时触发
onclick 在通知被点击时触发
onclose 在通知消失或被close()关闭时触发
onerror 在通知发生错误时触发
20.8 Page Visibility API
Page Visibility API为开发者提供页面是否可见的信息
组成部分:
document.visibilityState,表示:
页面被最小化了 hidden
页面在前台 visible
页面被隐藏了 prerender
visibilitychange: 可见状态变化时触发
document.hidden: 表示页面是否隐藏
2.9 Streams API
流:大块数据可以分成小部分处理
20.9.1 理解流
Stream定义了三种流:
可读流: 可以通过某个数据接口读取数据块的流,由消费者处理
可写流: 可以通过某个数据接口写入数据块的流,由生产者写入
转换流: 由上面两种流组成,这两个流之间是转换程序
流的基本单位是块
如果入列速度大于出列速度,会使用反压通知流入口停止发送,直到降低到某个阈值之下,这个阈值叫高水位线
20.9.2 可读流
ReadableStreamDefaultController
const readableStream = new ReadableStream({
start(controller){
console.log(controller); //ReadableStreamDefaultController
}
})
调用控制器的enqueue() 方法可以把值传入控制器
传完值后使用close()关闭流
async function * ints(){
for(let i=0;i<5;i++){
yield await new Promise((resolve)=>setTimeout(resolve,1000,i))
}
}
const readableStream = new ReadableStream({
async start(controller){
for await(let chunk of ints()){
controller.enqueue(chunk)
}
controller.close()
}
})
ReadableStreamDefaultReader
该实例可以通过流的getReader()方法获取,调用这个方法会获得流的锁,保证只有这个读取器可以从流中读取值
async function * ints(){
for(let i=0;i<5;i++){
yield await new Promise((resolve)=>setTimeout(resolve,1000,i))
}
}
const readableStream = new ReadableStream({
async start(controller){
for await(let chunk of ints()){
controller.enqueue(chunk)
}
controller.close()
}
})
console.log(readableStream.locked);// false
const readableStreamDefaultReader = readableStream.getReader()
console.log(readableStream.locked);// true
使用read()方法可以读出值
<div draggable="true">123</div>
<script>
async function* ints() {
for (let i = 0; i < 5; i++) {
yield await new Promise((resolve) => setTimeout(resolve, 1000, i))
}
}
const readableStream = new ReadableStream({
async start(controller) {
for await (let chunk of ints()) {
controller.enqueue(chunk)
}
controller.close()
},
})
console.log(readableStream.locked) // false
const readableStreamDefaultReader = readableStream.getReader()
console.log(readableStream.locked) // true
// 消费者
(async function () {
while (true) {
const { done, value } = await readableStreamDefaultReader.read()
if (done) {
break
} else {
console.log(value)
}
}
})()
20.9.3 可写流
20.9.4 转换流
20.9.5 管道连接流
要用再看把
20.10 计时API
Performance接口暴露了浏览器内部的度量指标
Performance接口API:
High Resolution Time APIperformance.now() 采用相对度量
Performance Timeline API
navigationTiming API
User Timing API
Resource Timing API
Paint Timing API
20.11 Web组件
这里的Web组件是一套用来增强DOM行为的工具
20.11.1 HTML模板
提前在页面中写出特殊标记,让浏览器自动将其解析为DOM子树
使用<template>模板
<template id="foo">
<p>I'm inside</p>
</template>
1.DocumentFragment
上面的例子不会被渲染到页面上,因为<template>内容不属于活动文档
使用content可以取得DocumentFragment的引用
let fragment = document.querySelector("#foo").content
可以使用fragment来添加新元素
使用importNode()复制DocumentFragment到div上
脚本执行可以推迟DocumentFragment的内容实际添加到DOM树
20.11.2 影子DOM
可以通过影子DOM把一棵完整的DOM树添加到父DOM树
影子DOM是通过attachShadow()创建并添加给有效HTML元素的
有效HTML标签:p652
影子DOM必须包含一个mode属性,open可以通过shadowRoot获取,closed则不行
但是不能为了安全而创建影子DOM
影子DOM可以在DOM树之间无限制移动
要使用<slot>来指定在哪里放置原来的HTML
可以使用命名槽位来指定多个投射
在影子DOM中发生的事件会逃避并通过重定向在外部被处理
20.11.3 自定义元素
浏览器会尝试把无法识别的元素作为通用元素整合进DOM
自定义元素在此基础上更进一步,可以为其定义复杂的行为,也可以把它纳入生命周期管理
调用customElements.define()方法可以创建自定义元素
可以为自定义元素添加影子DOM并添加内容
自定义元素的生命周期方法:p660
要从JS对象反射到DOM元素,要使用获取和设置函数
可以使用CustomElementRegistry的额外方法升级自定义元素
20.12 Web Cryptography API
描述了一套密码学工具,规范了JS如何以安全和符合惯例的方式实现加密
Math.random()是伪随机数生成器
CSPRNG额外增加了一个熵作为输入,虽然速度变慢了,但是安全性得到了极大提高
可以通过crypto.getRandomValues()访问
Web Cryptography API的重要特性都包含在SubtleCrypto对象上
可以通过window.crypto.subtle访问
包含常用的密码学方法
SubtleCrypto对象使用CryptoKey类的实例生成密钥
SubtleCrypto.generateKey()随机生成CryptoKey
使用exportKey()并指定格式就可以取得密钥
和exportKey()相反的操作使用importKey()实现
deriveKey() 和 deriveBits() 可以通过已配置的属性从已有密钥获得新密钥
SubtleCrypto.sign() 和 SubtleCrypto.verify() 可以生成签名或者验证签名
SubtleCrypto.encrypt() 和 SubtleCrypto.decrypt() 加密/解密
SubtleCrypto.warpKey() 和 SubtleCrypto.unwarpKey() 包装/解包