今天跟随B站UP主/公众号程序员Sunday过了一遍被称为JS红宝书的《JavaScript 高级程序设计(第四版)》,但其实除了前面几张,大部分内容都没有听懂。因此,下载了Sunday老师github发布的文档,下载链接:https://github.com/lgd8981289/book_read_quickly。
下载的Sunday老师的部分文档内容如下,也欢迎大家关注我的公众号GISideas,获取更多内容啊!
一小时读完红宝书
《JavaScript 高级程序设计(第四版)》一直被认为是 js 的圣经。里面包含了几乎所有 js 的知识和实用技术。特别适合想要学习或者精进 JS 的开发者阅读。
hello,大家好,我是 Sunday。
有很多小伙伴跟我留言说:Sunday 老师,讲一下红宝书吧。
那么这一次,它来啦~
整个红宝书的内容其实是非常多的,一共有 28 章。
整体的内容全部都是围绕着 js 展开。从最基础的《什么是 js 开始》,中间涉及到《动画、canvas》,到最后讲到《网络请求、worker 线程、最佳实践》。几乎是涵盖了整个 js 所有需要学习的内容。
如果你在工作中,对 js 有哪些不明确的地方,那么它应该都可以给你提供一些文档支持。
不过 ,28 章的内容确实有点多,所以我们把它划分为了三大部分:
- 基础部分:主要讲解
JS
基础知识,1 - 11 章 API
部分:主要讲解 浏览器、DOM 等相关的 API。12 - 20 章- 开发实用技术:主要讲解了一下开发的实用方案。
那么下面,咱们就来看看,这本一直被人称赞的《红宝书》。
一:基础部分
整本书从第一章开始,到第十一章为止。所讲解的都是 js
最基础的部分。
01:什么是 JavaScript
这一章主要讲解了两部分的内容:
- js 基本概念
- es 基本概念
咱们主要来看 js
就可以。
js
包含三部分:
-
ECMAScript
也就是es
:es
被称为是js
的核心,在2015 年
之后几乎保持着每年发布一版的频率。其中2015 年
发布的ESMAScript 第六版
被称为ES6
,如果用年费来表示的话就是ES 2015
。后续也同样维持着同样的叫法,比如:2016 年
发布第七版,被称作ES7
或ES 2016
-
DOM
: 文档对象模型。它是一组应用编程接口的API
,可以直接控制整个页面的节点。
比如,这样一段HTML
页面:通过
DOM
就可以表示为一组分层节点: -
BOM
:它表示 浏览器对象模型 的API
。主要用来操作浏览器窗口。比如我们常用的window
对象,就属于BOM
对象。
02:HTML 中的 JavaScript
核心内容分为了 3 个部分:
- 使用
<script>
元素的方式: 引入<script>
标签一般可以在两个地方做:- 在
header
标签的最后:但是这样会意味着,浏览器必须把所有JavaScript
代码都下载、解析完成后,才能开始渲染页面。这就会导致在最初时页面空白。 - 在
body
标签的最后:这样就不会影响页面渲染了。
- 在
- 行内脚本和外部脚本的区别: 所谓行内脚本就是直接把
JS
写到html
文件里面。这种方式肯定是不被推荐的。推荐使用外部脚本,也就是:以script
标签的形式引入。 js
不可用时,如何保证用户体验:html
提供了一个noscript
标签,可以在 不支持脚本运行 的浏览器中显示。
03:语言基础
这一章主要讲解的就是 JS
中的一些基础语法,比如:如何声明变量啦、如何判断数据类型啦。
整体的内容因为偏向基础部分,所以咱们主要就挑选其中一些易错的语法,来给大家进行讲解。
这些语法一共有:
-
typeof
操作符:typeof
是用来判断数据类型的语法。它的返回值是固定的几种类型。同时需要注意typeof null === 'object'
-
位操作符:位操作符一向是很多小伙伴不太熟悉的领域。所谓的 “位”,指的是 操作内存中表示数据的比特(位)。一般以
32 位
的整数进行表示。咱们来看下面这个图:
10010
表示为十进制的18
。那么咱们来看一下它的计算方式:这里的每一位都代表 2 的幂, 第一位(称为第 0 位)表示
2^0
,第二位表示2^1
,依此类推。如果一个位是空的,则以
0
填充,相当于忽略不计。
除此之外,作者还在书中提到了7
种按位运算。- 按位非:用波浪符(~)表示,它的作用是返回数值的一补数。即:由0变为1。
- 按位与:按位与操作在两个位都是 1 时返回 1,在任何一位是 0 时返回 0。
- 按位或:按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0。
- 按位异或:按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。
- 左移:按照指定的位数将数值的所有位向左移动
- 有符号右移:会将数值的所有 32 位都向右移,同时保留符号
- 无符号右移:会将数值的所有 32 位都向右移
-
with
语句:咱们在vuejs 设计与实现
的编译器环节看到过。with
语句的用途是将代码作用域设置为特定的对象。
04:变量、作用域与内存
本章的内容主要就是三部分:
- 变量: 变量分为两种类型:
- 简单数据类型:数据保存在栈中,通过变量直接引用
- 复杂数据类型:数据保存在堆中,变量无法直接引用到堆,所以需要在栈中保存一个内存地址,通过内存地址指向堆内存。
- 作用域: 作用域没有什么复杂度,主要理解上下文即可。
- 内存: 指的就是 垃圾回收机制。大致分为两种:
- 引用计数:在 循环引用 时出现错误
- 标记清理:目前浏览器中最常用的垃圾回收策略
05:基本引用类型
所谓基本引用类型,就是一些基本的 构造函数使用,这里作者提供了几个基本的对象,并且讲解了一些它们的用法。
咱们可以根据脑图大致的过一下。
06:集合引用类型
集合引用类型的内容就比较多了。
主要集中在 其他引用类型 部分。这部分内容一共分为 7 类:
- ArrayBuffer:
ArrayBuffer()
是一个普通的JavaScript
构造函数,可用于 在内存中分配特定数量的字节空间 。 - DataView:可以从二进制
ArrayBuffer
对象中读写多种数值类型的底层接口 - 定型数组:定型数组是另一种形式的
ArrayBuffer
视图 - Map:具备真正的 键/值 存储机制。
- WeakMap:表示“弱映射”的集合。
WeakMap
中的“weak”
(弱),描述的是JavaScript
垃圾回收程序对待“弱映射”中键的方式。意思就是,这些键不属于正式的引用,不会阻止垃圾回收。 - Set:值永远都不会重复的数组
- WeakSet:“弱集合”
07:迭代器与生成器
迭代器和生成器。这两个特性都是在 ES6
新增的,目的就是:可以更加方便的实现迭代。
所谓迭代指的就是:按照顺序反复多次执行一段程序,通常会有明确的终止条件。 比如常见的循环就是迭代器的一种简单实现。
但是基本的循环方式,随着代码量增加,代码会变得越发混乱。很多语言都通过原生语言结构解决了这个问题,开发者无须事先知道如何迭代就能实现迭代操作。这个解决方案就是 迭代器模式 。
迭代器模式描述了一个方案,即 可以把有些结构称为“可迭代对象”(iterable),因为它们实现了正式的 Iterable
接口,而且可以通过迭代器 Iterator
消费。
而生成器的形式是一个 函数,函数名称前面加一个星号**(*)**表示它是一个生成器。只要是可以定义函数的地方,就可以定义生成器。
生成器会返回一个迭代器对象。
生成器中存在一个 yield
关键字。
yield
关键字可以让生成器停止和开始执行,也是生成器最有用的地方。
生成器函数在遇到 yield
关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数只能通过在生成器对象上调用 next()
方法来恢复执行:
08:对象、类与面向对象编程
本章的主要内容有三部分:
- 对象的创建过程
- js 的继承机制
- js 中的类
对象的创建过程:
在这里作者通过 4 种模式来讲解了对象的创建过程
- 工厂模式:通过一个 函数(工厂) 不断地生成新的对象
- 构造函数模式:构造函数模式配合
new
关键字使用 - 原型模式:每个函数都会创建一个
prototype
属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型: - 对象迭代:直接通过一个包含所有属性和方法的对象字面量来重写原型成为了一种常见的做法
js 的继承机制:
JS 的继承主要是通过 原型链 实现的。其基本思想就是通过 原型继承多个引用类型的属性和方法。
除此之外,作者还提到了 4 中继承方式:
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生式组合继承
js 中的类:
类就是 class
的意思,它是 ES6
之后新增的语法糖,背后使用的仍然是原型和构造函数的概念。这也是目前最推荐的实现继承的方式。
与函数类型相似,定义类也有两种主要方式:类声明和类表达式。这两种方式都使用 class 关键字加大括号:

类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但这些都不是必需的。空的类定义照样有效。

09:代理与反射
所谓代理指的就是 Proxy
,所谓反射指的就是 Reflect
。这两个对象咱们之前在书中都有讲解过。
那么下面,咱们就来看下《红宝书》中对代理和反射的解释。
代理:
红宝书首先对代理进行了定义,书中提到:代理是目标对象的抽象。咱们来看这段代码:

在这段代码中,proxy
就被称为代理,也叫做代理对象。target
被叫做目标对象。handler
里可以写入代理行为,被称为 捕获器。
想要代理对象,那么主要是通过捕获器(handler
)进行的:

那么此时,只要 proxy
触发了 get()
操作,就会触发 get()
捕获器。
所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。比如,get()
捕获器会接收到目标对象、要查询的属性和代理对象三个参数。

有了这些参数,就可以直接利用代理对象获取原始对象的属性:

这些就是基本的代理逻辑。
反射:
处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API 方法。
比如:可以直接通过 Reflect.get
获取对象的属性。

这里的 arguments
是 trapTarget, property, receiver
这三个参数。这里就等同于把这三个参数直接放到了 get
方法中。
接下来书中又详细介绍了 Reflect
的更多 API
使用。这里咱们就不一个一个去说了。
10:函数
书中在本章开头就提到:函数是 ECMAScript
中最有意思的部分之一,这主要是因为函数实际上是对象。每个函数都是Function
类型的实例,而 Function
也有属性和方法,跟其他引用类型一样。
函数主要有四种定义方式:
首先第一种就是通过 function
关键字 :因为函数是对象,所以函数名就是指向函数对象的指针,而且不一定与函数本身紧密绑定。

第二种是:函数表达式。

第三种就是:箭头函数。

最后一种是使用 Function
构造函数。

注意: 这种方式是不被推荐的。
因为这段代码会被解释两次:第一次是将它当作常规 ECMAScript
代码,第二次是解释传给构造函数的字符串。这显然会影响性能。
递归:
递归函数通常的形式是:一个函数通过名称调用自己

这是经典的递归阶乘函数。
闭包:
闭包指的是:那些 引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。

这里加粗的代码位于内部函数(匿名函数)中,其中引用了外部函数的变量 propertyName
。那么这个内部函数就可以被叫做 闭包。
11:期约与异步函数
所谓期约指的是: promise
。
我们知道 promise
主要就是处理异步编程的,所以,本章的内容主要就是三部分:
- 异步编程
promise
期约- 异步函数
异步编程:
在早期的 JavaScript
中,只支持定义回调函数来表明异步操作完成。串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(俗称“回调地狱”)来解决。
比如,这种代码:

promise:
而 promise
的出现就是为了解决这种回调地狱的问题。
ECMAScript 6 新增的引用类型 Promise
,可以通过 new 操作符来实例化。

它的内部有三种状态:
- 待定(
pending
) - 兑现(fulfilled,resolved)
- 拒绝(
rejected
)
promise
具有两个实例方法:
p.then
:可以处理 已兑现 状态
p.catch
:可以处理 已拒绝 状态
异步函数
所谓异步函数指的就是 “async/await”(语法关键字),它主要是用来配合 promise
进行使用的。
其中 async
关键字用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上:

使用 async 关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。
而 await
关键字,必须要在 异步函数 中使用。
用 await
关键字可以暂停异步函数代码的执行,等待 promise
解决。

第一部分总结
那么到这里,咱们就把第一部分讲解完了。这一部分主要就是 js
的基本语法。
接下来咱们来去看第二部分:API 部分
二、API 部分
这一部分描述了从 12 章到 20 章的内容。
12:BOM
根据章名咱们也可以看出来,这一章主要就是讲解 BOM 对象的。
核心的内容有 4 部分:
- window 对象
- location 对象
- navigator 对象
- history 对象
window 对象
BOM
的核心是 window
对象,表示浏览器的实例。
window
对象在浏览器中有两重身份,一个是 ECMAScript
中的 Global
对象,另一个就是浏览器窗口的 JavaScript
接口。这意味着网页中定义的所有对象、变量和函数都以 window
作为其 Global
对象进行访问。
因为 window
对象被复用为 ECMAScript
的 Global
对象,所以通过 var
声明的所有全局变量和函数都会变成 window
对象的属性和方法。

这里,变量 age 和函数 sayAge()被定义在全局作用域中,它们自动成为了 window 对象的成员。
但是需要注意:如果在这里使用 let 或 const 替代 var,则不会把变量添加给全局对象:

location 对象
location
是最有用的 BOM
对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能。
这个对象独特的地方在于,它既是 window 的属性,也是 document 的属性。也就是说,window.location 和 document.location 指向同一个对象。location 对象不仅保存着当前加载文档的信息,也保存着把 URL 解析为离散片段后能够通过属性访问的信息。

navigator 对象
它的属性通常用于确定 浏览器的类型。
它提供了非常多的属性和方法:


history 对象
history
对象表示当前窗口首次使用以来用户的导航历史记录。因为 history
是 window
的属性,所以每个 window
都有自己的 history
对象。
通常情况下,我们可以通过它来控制 URL
的前进和后退。
比如,他所提供的 go
方法:

go()有两个简写方法:back() 和 forward()。顾名思义,这两个方法模拟了浏览器的后退按钮和前进按钮:

13:客户端检测
这里的客户端其实指的就是 浏览器 。而所谓的检测主要有三部分:
- 能力检测
- 用户代理检测
- 软件与硬件检测
能力检测:
能力检测(又称特性检测)即在 JavaScript 运行时中使用一套简单的检测逻辑,测试浏览器是否支持某种特性。
能力检测的基本模式如下,通过这个代码可以检测浏览器是否拥有 propertyInQuestion
方法:

用户代理检测:
用户代理检测:通过浏览器的用户代理字符串确定使用的是什么浏览器。
想要知道自己代码运行在什么浏览器上,大部分开发者会分析 window.navigator.userAgent
返回的字符串值。

软件与硬件检测:
现代浏览器提供了一组与页面执行环境相关的信息,包括浏览器、操作系统、硬件和周边设备信息。这些属性可以通过暴露在 window.navigator 上的一组 API 获得。
这一部分提供了很多的 API
,咱们就不一一介绍了。
14:DOM
DOM
被称为是:文档对象模型。
可以通过 DOM
来表示 由多层节点构成的文档,通过它开发者可以添加、删除和修改页面的各个部分。
本章的主要内容包含了三部分:
- 节点层级
- DOM 编程
- MutationObserver 接口
节点层级
任何 HTML 或 XML 文档都可以用 DOM 表示为一个由节点构成的层级结构。
节点分很多类型,每种类型对应着文档中不同的信息和(或)标记,也都有自己不同的特性、数据和方法,而且与其他类型有某种关系。
这些关系构成了层级,让标记可以表示为一个以特定节点为根的树形结构。
以下面的 HTML为例:

如果用层级表示,则是这样的:

DOM 编程
所谓的 DOM 编程
其实指的就是:通过 JS 操作 DOM。
咱们来看一个 动态插入 <script>
标签的例子:

<script>
元素用于向网页中插入 JavaScript 代码,可以是 src 属性包含的外部文件,也可以是作为该元素内容的源代码。
MutationObserver 接口
MutationObserver 接口,可以在 DOM 被修改时异步执行回调。使用 MutationObserver 可以观察整个文档、DOM 树的一部分,或某个元素。
书中详细的讲解了它的基本用法:
MutationObserver 的实例要通过调用 MutationObserver 构造函数并传入一个回调函数来创建:
observe 方法
新创建的 MutationObserver
实例不会关联 DOM
的任何部分。
要把这个 observer
与 DOM
关联起来,需要使用 observe()
方法。
这个方法接收两个必需的参数:
- 要观察其变化的 DOM 节点
- 一个 配置 对象。
执行以上代码后,<body>
元素上任何属性发生变化都会被这个 MutationObserver
实例发现,然后就会异步执行注册的回调函数。
15:DOM 扩展
本章的内容主要就是两部分:
Selectors API
HTML5 DOM
扩展
Selectors API
Selectors API
主要指的就是获取 DOM 元素
的方法,这里的 API
主要有三个:
querySelector()
querySelectorAll()
matches()
:接收一个CSS
选择符参数,如果元素匹配则该选择符返回true
,否则返回false
。
HTML5 DOM
扩展
HTML5
新增了很多的扩展内容,比如:
-
CSS 类扩展
:比如dcument.getElementsByClassName()
-
焦点管理:比如
dom.focus()
获取焦点的方法 -
HTMLDocument 扩展:比如
document.head
获取<header>
标签元素 -
字符集属性:比如
document.characterSet
获取字符集(UTF-16 || UTF-8
) -
自定义数据属性:比如 使用前缀
data-
以便告诉浏览器,这些属性既不包含与渲染有关的信息,也不包含元素的语义信息。 -
插入标记:比如
innterHTML
属性 -
scrollIntoView()
:这个API
可能很多小伙伴没有遇到过,它的作用是:滚动选中元素的父元素,使选中元素可以被用户可见。document.getElementById('nav-footer').scrollIntoView();
比如我们可以通过这种方式,来让
nav-footer
元素被用户可见。
16:DOM2 和 DOM3
本章主要讲解了三部分的内容:
- DOM2 到 DOM3 的变化
- 操作样式的 DOM API
- DOM 遍历与范围
DOM2 到 DOM3 的变化
DOM2
和 DOM3
指的其实是 DOM 2级
和 DOM 3级
。
所谓的每一级其实指的是 在原有的级别上增加了一些扩充的事件。
比如:DOM1(DOM Level 1)主要定义了 HTML 和 XML 文档的底层结构。
DOM2
级在原来 DOM
的基础上又扩充了鼠标、用户界面事件、范围、遍历等细分模块。
DOM3
进一步扩展了 DOM
,新增了比如:DOM
加载和保存模块、DOM验证模块 等等。
**操作样式的 DOM API **
任何支持 style
属性的 HTML
元素在 JavaScript
中都会有一个对应的 style
属性。
HTML style
属性中的 CSS
属性在 JavaScript style
对象中都有对应的属性:

这个表中列举出来的属性知识一部分,实际的属性非常多,咱们就不一个一个进行列举了。
DOM 遍历与范围
DOM 遍历是对 DOM 结构的深度优先遍历,至少允许朝两个方向移动(取决于类型)。
遍历以给定节点为根,不能在 DOM 中向上超越这个根节点:
我们以这段代码为例:

这段代码构成的 DOM 树如图

其中的任何节点都可以成为遍历的根节点。
比如,假设以<body>
元素作为遍历的根节点,那么接下来是<p>
元素、<b>
元素和两个文本节点(都是<body>
元素的后代)。
但这个遍历不会到达<html>
元素、<head>
元素,或者其他不属于<body>
元素子树的元素。
而以 document
为根节点的遍历,则可以访问到文档中的所有节点:

17:事件
主要是五个部分:
- 事件流
- 使用事件处理程序
- 事件对象
- 事件类型
- 事件委托
事件流
事件流描述了 页面接收事件的顺序。大体分为两类:
- 事件冒泡
- 事件捕获
事件冒泡
IE 事件流被称为事件冒泡:事件被定义为从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。
我们以这个代码为例:

它的事件流传输过程为:

事件捕获
事件捕获的意思是:最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。
同样以这个示例:

它的事件流传输过程为:

在现在的 DOM
事件流中,一个完整的事件流程被分为三个阶段:事件捕获、到达目标和事件冒泡。

使用事件处理程序
事件意味着 用户或浏览器执行的某种动作(click
、load
)。
为响应事件而调用的函数 被称为事件处理程序(或事件监听器)。
事件处理程序的名字以"on"
开头,因此 click
事件的处理程序叫作 onclick
,而 load
事件的处理程序叫作 onload
。
事件对象
在 DOM
中发生事件时,所有相关信息都会被收集并存储在一个名为 event
的对象中。这个 event
对象就被成为 事件对象。
在 DOM
合规的浏览器中,event
对象是传给事件处理程序的唯一参数。
不管以哪种方式指定事件处理程序,都会传入这个 event
对象。

事件类型
DOM
中的事件类型有很多。作者在书中提到, DOM3
定义的事件类型有以下 7 种
其中每一个大类型还包含了很多具体的事件类型。这块的内容非常的多,涉及到了各种事件的使用方式。
咱们就不一个一个去说了。
事件委托
在 JavaScript
中,页面中事件处理程序的数量与页面整体性能直接相关,原因大体有两点:
- 首先:每个函数都是对象,都占用内存空间,对象越多,性能越差。
- 其次:为指定事件处理程序所需访问 DOM 的次数会先期造成整个页面交互的延迟。
那么想要处理这种情况,作者给我们提供了一种方式,就是 事件委托
事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。
比如:

这里的 HTML 包含 3
个列表项,在被点击时应该执行某个操作。对此,通常的做法是像这样指定 3 个事件处理程序(非事件委托):

如果对页面中所有需要使用 onclick 事件处理程序的元素都如法炮制,结果就会出现大片雷同的代码。
使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题:

18:动画与 Canvas 图形
主要分为4部分:
- requestAnimationFrame API
<canvas>
标签- 绘制 2D 图形的方式
- WebGL 绘制 3D 图形
本章的内容其实是比较复杂的,涉及到了 canvas、webgl
等 API
的使用。
其中后三部分涉及到了大量的 API
,咱们没有办法在这次视频中进行讲解。所以针对本章主要就说明下第一部分 requestAnimationFrame API
:
requestAnimationFrame 是一个配合动画执行的 API
。
它会在浏览器下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
在没有 requestAnimationFrame
之前, JavaScript
中创建动画基本上就是使用 setInterval()
来控制动画的执行。

比如,上面的代码:每间隔 100 毫秒执行特定的方法。
但是,这样的代码其实是有一些问题的。比如 时间间隔 的问题。
我们知道 setInterval
的第二个参数的延时,只能保证何时会把代码添加到浏览器的任务队列,不能保证添加到队列就会立即运行。
如果队列前面还有其他任务,那么就要等这些任务执行完再执行。
简单来讲,这里毫秒延时并不是说何时这些代码会执行,而只是说到时候会把回调加到任务队列。
如果添加到队列后,主线程还被其他任务占用,比如正在处理用户操作,那么回调就不会马上执行。
那么就可能导致动画不够平滑。
而 requestAnimationFrame
则会在浏览器下一次重绘之前执行,这样就保证了执行时机的问题。
19:表单脚本
内容大致分为了三部分:
- 表单基础
- 一些表单控件的验证和交互用法
- 富文本编辑
表单基础
Web
表单在 HTML
中以<form>
元素表示,在 JavaScript
中则以 HTMLFormElement
类型表示。
比如,我们可以通过简单的 getElementById()
来获取表单:
接下来作者又提到了一些表单的基础行为,比如:
- 表单提交
- 表单验证
- 重置表单
一些表单控件的验证和交互用法
表单的控件有很多,比如:
- 文本框控件:单行可以使用
input
元素,多行可以使用textarea
元素 - 选择框控件:
<select>和<option>
除此之外,表单控件还提供了很多的方法:
-
选中文本:
select()
方法
-
屏蔽特殊文本:验证
charCode
: -
约束验证:
required
标记:
这部分内容并不复杂,属于基础的东西。所以咱们把一些关键的场景列举出来,就不再细说了。
富文本编辑
富文本编辑在现在的开发中是一个常用的需求。咱们大多数时候都会通过一些 富文本的库来去实现这个功能。
作者在书中介绍了一些 富文本编辑的底层实现逻辑,咱们一起来看一下。
富文本实现的基本技术就是:在空白 HTML
文件中嵌入一个 iframe
。

通过 designMode
属性,可以将这个空白文档变成可以编辑的,实际编辑的则是<body>
元素的 HTML
。
designMode
属性有两个可能的值:"off"
(默认值)和"on"
。
设置为"on"
时,整个文档都会变成可以编辑的(显示插入光标),从而可以像使用文字处理程序一样编辑文本,通过键盘将文本标记为粗体、斜体,等等。
20:JavaScript API
这一章的内容特别的多,详细的介绍了 JS
的各种 API
使用方式,总共包含了12 类:
- Atomics 与 SharedArrayBuffer:
- SharedArrayBuffer:用来表示一个通用的、固定长度的原始二进制数据缓冲区
- Atomics:强制同一时刻只能对缓冲区执行一个操作,可以让多个上下文安全地读写一个
SharedArrayBuffer
。
- **跨上下文消息:**在不同执行上下文(如不同工作线程或不同源的页面)间传递信息的能力
- Encoding API: 实现字符串与定型数组之间的转换
- File API 与 Blob API:
- File API:文件本身
- Blob API:文件数据对象(数据本身)
- **媒体元素:**即 和 ,从而为浏览器提供了嵌入音频和视频的统一解决方案。
- 拖放:
- dragstart
- drag
- dragend
- Notifications API: 用于向用户显示通知
- Page Visibility API: 页面对用户是否可见的信息
- Streams API: 把一个大数据块拆分为多个小数据块分开处理
- 计时 API: 检测页面性能指标
- **Web 组件:**一套用于增强
DOM
行为的工具,包括影子DOM
、自定义元素和HTML
模板 - **Web Cryptography API:**一套密码学工具,规范了
JavaScript
如何以安全和符合惯例的方式实现加密
第二部分总结
第二部分主要介绍了 浏览器、DOM 等相关的 API。
那么接下来咱们来看第三部分:开发实用技术。
三:开发实用技术
第三部分也是全书中最后一部分的内容。
21:错误处理与调试
本章的内容主要分为三个部分:
- 理解浏览器错误报告
- 处理错误
- 调试 JavaScript 代码
理解浏览器错误报告
浏览器的错误报告会 展示在控制台 中。
如果想要查看错误,那么需要进入到浏览器的控制台。
而在移动端上,想要查看错误报告,则需要使用到一些其他的工具。这里作者并没有详细给出介绍。只是提出了几个方案:
- Chrome 的操作步骤参见 Google Developers 网站的文章《Android 设备的远程调试入门》
- Safari 的操作步骤参见 Apple Developer 网站的文章“Safari Web Inspector Guide”。
- Firefox 常用的调试工具是 Firebug Lite
处理错误
在 JS
中想要捕获错误,那么需要通过 tey/catch
的形式进行捕获。

任何可能出错的代码都应该放到 try
块中,而处理错误的代码则放在 catch
块中。如果 try
块中有代码发生错误,代码会立即退出执行,并跳到 catch
块中。catch
块此时接收到一个对象,该对象包含发生错误的相关信息。
try/catch
语句中可选的 finally
子句始终运行。
如果 try
块中的代码运行完,则接着执行finally
块中的代码。如果出错并执行 catch
块中的代码,则 finally 块中的代码仍执行。try
或 catch
块无法阻止 finally
块执行,包括 return
语句。

调试 JavaScript 代码
这里作者提供了三种方式:
- 通过
console
进行控制台打印 - 通过
debugger
关键字调试 - 通过
throw new Error
抛出错误
22:处理 XML + 23:JSON
22和23 章的内容,咱们一块进行查看。
首先咱们先来看一下什么是 XML
:

XML
被设计用来传输和存储数据(和 JSON
类似)。以上的这段 XML
可以用以下 JSON
表示:

在现在的数据处理中,XML
已经非常少见了,大部分都是通过 JSON
进行处理。
所以说,对于 22
章的内容,咱们没有必要再去学习,直接查看 23章:json
即可。
json
是目前主流的数据传输格式,他支持以下几种形式:

- 简单值
- 对象
- 数组
JSON
支持序列化和解析。
所谓序列化指的是:通过 JSON.stringify()
方法,把一个对象变为 json
格式的字符串。
解析指的是:通过 JSON.parse()
方法,把 json
格式的字符串转化为 对象。
24:网络请求与远程资源
内容主要分成 5 个部分:
- 使用 XMLHttpRequest 对象
- 处理 XMLHttpRequest 事件
- 源域 Ajax 限制
- Fetch API
- WebSocket
使用 XMLHttpRequest 对象
XMLHttpRequest
简称为 XHR
,是处理 ajax
网络请求的基类。
下面这段代码,描述了 XHR
的基本使用:

在上面的代码中,使用 new
关键字得到了 xhr
的实例。
处理 XMLHttpRequest 事件
处理 XHR
事件指的是处理 进度事件。
这些事件一共有 6 种:
loadstart
:在接收到响应的第一个字节时触发。progress
:在接收响应期间反复触发。error
:在请求出错时触发。abort
:在调用abort()
终止连接时触发。load
:在成功接收完响应时触发。loadend
:在通信完成时,且在error
、abort
或load
之后触发。
想要处理事件,那么可以通过 xhr.
的形式处理,下面以 load 为例进行演示:

源域 Ajax 限制
通过 XHR
进行 Ajax
通信的一个主要限制是 跨源安全策略。默认情况下,XHR
只能访问与发起请求的页面在同一个域内的资源。
而想要解决这个问题,则需要通过 CORS
跨源资源共享(CORS,Cross-Origin Resource Sharing
)定义了 浏览器与服务器如何实现跨源通信。
**Fetch API **
直接的 XHR
请求比较复杂,所以也可以通过 ``Fetch API
进行处理。
Fetch API
是目前 js
原生支持的 ajax
请求对象。可以返回 promise
的实例:

目前 fetch API
在实际开发中使用并不会多,大多数会通过 axios
进行处理。
WebSocket
Web Socket(套接字)
的目标是:通过一个长时连接实现与服务器全双工、双向的通信。
在 JavaScript
中创建 Web Socket
时,一个 HTTP
请求会发送到服务器以初始化连接。
服务器响应后,连接使用的 HTTP 协议会切换到 Web Socket 协议。
这意味着 Web Socket
不能通过标准 HTTP
服务器实现,而必须使用支持该协议的专有服务器。
接下来咱们来看一段 web socket
的基本代码,分为:
- 创建链接
- 发送事件
- 响应事件

25:客户端存储
所谓的客户端存储大致分为三类:
- cookie
- 浏览器存储 API
- IndexedDB
cookie
cookie,最初用于 在客户端存储会话信息。
这个规范要求服务器在响应 HTTP
请求时,通过发送 Set-Cookie HTTP
头部包含会话信息

**浏览器存储 API **
所谓浏览器存储 API
指的就是 Web Storage
。
这个 Storage
分为:
- sessionStorage:只存储会话数据,这意味着数据只会存储到浏览器关闭。
- localStorage:作为在客户端持久存储数据的机制。数据不会跟随浏览器关闭消失。
sessionStorage
与 localStorage
提供了同样的操作方法,主要有以下四个:
clear()
:删除所有值;不在 Firefox 中实现。getItem(*name*)
:取得给定 name 的值。key(*index*)
:取得给定数值位置的名称。removeItem(*name*)
:删除给定 name 的名/值对。
IndexedDB
IndexedDB 是浏览器中存储结构化数据的一个方案。用于代替目前已废弃的 Web SQL Database API。
它是类似于 MySQL
的数据库。与传统数据库最大的区别在于, IndexedDB
使用 对象存储 而不是表格保存数据。IndexedDB
数据库就是在一个公共命名空间下的一组对象存储,类似于 NoSQL
风格(mongoDB)的实现。
作者在书中详细的介绍了 IndexedDB
的使用,包括:
- 如何创建对象存储
- 如何开启 事务
- 如何插入对象
- 如何使用游标查询
这些内容涉及到了很多具体的代码,咱们这里就不去细说了。
感兴趣的同学可以看下书中的介绍。
26:模块
本章的内容主要分为四部分:
- 理解模块模式
- 凑合的模块系统
- 使用前 ES6 模块加载器
- 使用 ES6 模块
理解模块模式
所谓模块化其实指的就是:把逻辑分块,各自封装,相互独立,每个块自行决定对外暴露什么,同时自行决定引入执行哪些外部代码。
这些不同的模块想要连接在一起工作,那么必须有一个 入口,也就是代码执行的起点。

图中的箭头表示依赖方向:模块 A 依赖模块 B 和模块 C,模块 B 依赖模块 D 和模块 E,模块 C 依赖模块 E。
因为模块必须在依赖加载完成后才能被加载,所以这个应用程序的入口模块 A 必须在应用程序的其他部分加载后才能执行。
凑合的模块系统
所谓 凑合的模块系统 指的是 IIFE(立即调用函数表达式)
的模块化方式:

这种立即执行函数可以通过外部传参的形式传递参数:

使用前 ES6 模块加载器
ES6
之前的模块加载器,指的主要是三部分:
CommonJS
AMD
UMD
咱们这里主要来看下 CommonJS
就可以。
CommonJS
模块语法不能在浏览器中直接运行,主要应用在 node
环境中。

CommonJS
模块定义需要使用 require()
指定依赖,而使用 exports
对象定义自己的公共 API
。
使用 ES6 模块
ES6 模块化咱们在其他的书中讲解过很多次了。这里咱们就不再多说了。给大家把脑图的内容展示即可。
27:工作者线程
工作者线程是一套比较复杂的内容,大致可以分为四个部分:
- 工作者线程简介
- 使用专用工作者线程执行后台任务
- 使用共享的工作者线程
- 通过服务工作者线程管理请求
工作者线程简介
JavaScript
环境实际上是运行在托管操作系统中的虚拟环境。
在浏览器中每打开一个页面,就会分配一个它自己的环境。
这样,每个页面都有自己的内存、事件循环、DOM,等等。
每个页面就相当于一个沙盒,不会干扰其他页面。每个页面环境都是 并行执行 的。
而工作者线程的作用就是:让浏览器可以在原始页面环境之外再分配一个完全独立的二级子环境。这个子环境 不能 与依赖单线程交互的 API(如 DOM)互操作,但可以与父环境 并行执行代码。简单来说,就是让 JS
拥有了 类似多线程 的能力。
工作者线程可以分为三类:
- 专用工作者线程
- 共享工作者线程
- 服务工作者线程
这三类工作者线程,作者在书中详细的解释了他们的使用方式。但是因为涉及到的内容比较细节,所以说咱们这里就不去详解了。
28:最佳实践
本书中最后一章的内容,更多的是一些 思想层面 的描述。
内容主要有三部分:
- 可维护性
- 性能
- 部署
可维护性
通常,说代码“可维护”就意味着它具备如下特点:
- 容易理解:无须求助原始开发者,任何人一看代码就知道它是干什么的,以及它是怎么实现的。
- 符合常识:代码中的一切都显得顺理成章,无论操作有多么复杂。
- 容易适配:即使数据发生变化也不用完全重写。
- 容易扩展:代码架构经过认真设计,支持未来扩展核心功能。
- 容易调试:出问题时,代码可以给出明确的信息,通过它能直接定位问题。
而想要实现这五点,则需要从 编码规范 和 松散解耦 两个方向入手。
所谓编码规范指的就是:一套规定代码如何编写的方案。这个方案不同的企业可能会有细微差异。
而松散解耦指的就是:不要让代码过于耦合。
性能
关于性能的优化,作者主要提出了四个方面:
- 作用域意识:访问全局变量始终比访问局部变量慢,因为必须遍历作用域链。任何可以缩短遍历作用域链时间的举措都能提升代码性能
- 选择正确的方法:使用最优的计算方法(感觉像废话)。在这里作者介绍了几种方式,比如:简化终止条件、简化循环体、使用后测试循环。但是想要达到这些条件,需要开发者拥有比较好的算法基础。
- 语句最少化:语句的数量影响操作执行的速度。一条可以执行多个操作的语句,比多条语句中每个语句执行一个操作要快。
- 优化 DOM 交互:涉及 DOM 操作的部分是最慢的,所以想要提升性能则尽量减少 DOM 操作
部署
所谓部署就是把 web 应用程序
发布到线上环境。
整体流程大致分为三步:
- 构建:把代码进行打包。可以使用
webpack
等打包工具进行。 - 验证:这些错误主要指的是一些 运行时的错误 或 语法错误 。
- 压缩:指的就是代码压缩。比如可以删除一些 空格、换行、注释 的内容。这些内容都会影响最终代码的大小。
总结与下期预告
那么到这里,咱们就把红宝书中的所有内容给看完啦。
其实想要把整本红宝书中的内容全部整理出来,并且压缩到一个小时左右的时间,是一件挺难得事情。
之前全职工作的时候,其实很难有这么多的时间和精力去做这件事情。
这也就导致之前虽然答应了一些小伙伴要录制红宝书,但是一直没有录制出来的原因。
那么现在,这个 flag 可算是完成了。
在下一期,咱们会去讲这一本书《程序员超强大脑》。
它主要讲解了 程序员认知 层面的问题。因为 程序设计本质上是一个认知过程。
如果我们可以理解这种认知过程,那么就可以大幅度提高咱们的工作学习效率。
那么咱们在下一本书中,再见咯~~
最后一章的内容,更多的是一些 思想层面 的描述。
内容主要有三部分:
- 可维护性
- 性能
- 部署
可维护性
通常,说代码“可维护”就意味着它具备如下特点:
- 容易理解:无须求助原始开发者,任何人一看代码就知道它是干什么的,以及它是怎么实现的。
- 符合常识:代码中的一切都显得顺理成章,无论操作有多么复杂。
- 容易适配:即使数据发生变化也不用完全重写。
- 容易扩展:代码架构经过认真设计,支持未来扩展核心功能。
- 容易调试:出问题时,代码可以给出明确的信息,通过它能直接定位问题。
而想要实现这五点,则需要从 编码规范 和 松散解耦 两个方向入手。
所谓编码规范指的就是:一套规定代码如何编写的方案。这个方案不同的企业可能会有细微差异。
而松散解耦指的就是:不要让代码过于耦合。
性能
关于性能的优化,作者主要提出了四个方面:
- 作用域意识:访问全局变量始终比访问局部变量慢,因为必须遍历作用域链。任何可以缩短遍历作用域链时间的举措都能提升代码性能
- 选择正确的方法:使用最优的计算方法(感觉像废话)。在这里作者介绍了几种方式,比如:简化终止条件、简化循环体、使用后测试循环。但是想要达到这些条件,需要开发者拥有比较好的算法基础。
- 语句最少化:语句的数量影响操作执行的速度。一条可以执行多个操作的语句,比多条语句中每个语句执行一个操作要快。
- 优化 DOM 交互:涉及 DOM 操作的部分是最慢的,所以想要提升性能则尽量减少 DOM 操作
部署
所谓部署就是把 web 应用程序
发布到线上环境。
整体流程大致分为三步:
- 构建:把代码进行打包。可以使用
webpack
等打包工具进行。 - 验证:这些错误主要指的是一些 运行时的错误 或 语法错误 。
- 压缩:指的就是代码压缩。比如可以删除一些 空格、换行、注释 的内容。这些内容都会影响最终代码的大小。