diff --git a/.github/workflows/wangdoc.yml b/.github/workflows/wangdoc.yml
new file mode 100644
index 0000000..c3e5cbe
--- /dev/null
+++ b/.github/workflows/wangdoc.yml
@@ -0,0 +1,36 @@
+name: JavaScript tutorial CI
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ page-generator:
+ name: Generating pages
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 'latest'
+ - name: Install dependencies
+ run: npm install
+ - name: Build pages
+ run: npm run build
+ - name: Deploy to website
+ uses: JamesIves/github-pages-deploy-action@v4
+ with:
+ git-config-name: wangdoc-bot
+ git-config-email: yifeng.ruan@gmail.com
+ repository-name: wangdoc/website
+ token: ${{ secrets.WANGDOC_BOT_TOKEN }}
+ branch: master # The branch the action should deploy to.
+ folder: dist # The folder the action should deploy.
+ target-folder: dist/javascript
+ clean: true # Automatically remove deleted files from the deploy branch
+ commit-message: update from JavaScript tutorial
+
diff --git a/.gitignore b/.gitignore
index c5ea0d4..01a7204 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules/
dist/
npm-debug.log
+package-lock.json
diff --git a/.travis.yml b/.travis.yml.bak
similarity index 100%
rename from .travis.yml
rename to .travis.yml.bak
diff --git a/docs/async/general.md b/docs/async/general.md
index d025df7..a745bfe 100644
--- a/docs/async/general.md
+++ b/docs/async/general.md
@@ -12,7 +12,7 @@ JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。
如果排队是因为计算量大,CPU 忙不过来,倒也算了,但是很多时候 CPU 是闲着的,因为 IO 操作(输入输出)很慢(比如 Ajax 操作从网络读取数据),不得不等着结果出来,再往下执行。JavaScript 语言的设计者意识到,这时 CPU 完全可以不管 IO 操作,挂起处于等待中的任务,先运行排在后面的任务。等到 IO 操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是 JavaScript 内部采用的“事件循环”机制(Event Loop)。
-单线程模型虽然对 JavaScript 构成了很大的限制,但也因此使它具备了其他语言不具备的优势。如果用得好,JavaScript 程序是不会出现堵塞的,这就是为什么 Node 可以用很少的资源,应付大流量访问的原因。
+单线程模型虽然对 JavaScript 构成了很大的限制,但也因此使它具备了其他语言不具备的优势。如果用得好,JavaScript 程序是不会出现堵塞的,这就是 Node.js 可以用很少的资源,应付大流量访问的原因。
为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。
@@ -271,7 +271,7 @@ function launcher() {
running--;
if(items.length > 0) {
launcher();
- } else if(running == 0) {
+ } else if(running === 0) {
final(results);
}
});
diff --git a/docs/async/promise.md b/docs/async/promise.md
index 68b5c07..cb629d7 100644
--- a/docs/async/promise.md
+++ b/docs/async/promise.md
@@ -274,7 +274,7 @@ console.log(3);
## 参考链接
-- Sebastian Porto, [Asynchronous JS: Callbacks, Listeners, Control Flow Libs and Promises](http://sporto.github.com/blog/2012/12/09/callbacks-listeners-promises/)
+- Sebastian Porto, [Asynchronous JS: Callbacks, Listeners, Control Flow Libs and Promises](https://sporto.github.io/blog/2012/12/09/callbacks-listeners-promises/)
- Rhys Brett-Bowen, [Promises/A+ - understanding the spec through implementation](http://modernjavascript.blogspot.com/2013/08/promisesa-understanding-by-doing.html)
- Matt Podwysocki, Amanda Silver, [Asynchronous Programming in JavaScript with “Promises”](http://blogs.msdn.com/b/ie/archive/2011/09/11/asynchronous-programming-in-javascript-with-promises.aspx)
- Marc Harter, [Promise A+ Implementation](https://gist.github.com//wavded/5692344)
diff --git a/docs/basic/grammar.md b/docs/basic/grammar.md
index fdf6f33..b0dea02 100644
--- a/docs/basic/grammar.md
+++ b/docs/basic/grammar.md
@@ -238,7 +238,7 @@ JavaScript 提供`if`结构和`switch`结构,完成条件判断,即只有满
### if 结构
-`if`结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是 JavaScript 的两个特殊值,`true`表示真,`false`表示`伪`。
+`if`结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是 JavaScript 的两个特殊值,`true`表示“真”,`false`表示“伪”。
```javascript
if (布尔值)
@@ -485,7 +485,7 @@ var msg = '数字' + n + '是' + (n % 2 === 0 ? '偶数' : '奇数');
### while 循环
-`While`语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。
+`while`语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。
```javascript
while (条件)
@@ -727,4 +727,4 @@ top:
## 参考链接
-- Axel Rauschmayer, [A quick overview of JavaScript](http://www.2ality.com/2011/10/javascript-overview.html)
+- Axel Rauschmayer, [Basic JavaScript for the impatient programmer](https://2ality.com/2013/06/basic-javascript.html)
diff --git a/docs/basic/history.md b/docs/basic/history.md
index 06a4131..f5c266b 100644
--- a/docs/basic/history.md
+++ b/docs/basic/history.md
@@ -125,7 +125,7 @@ JavaScript 伴随着互联网的发展一起发展。互联网周边技术的快
2007年,Webkit 引擎在 iPhone 手机中得到部署。它最初基于 KDE 项目,2003年苹果公司首先采用,2005年开源。这标志着 JavaScript 语言开始能在手机中使用了,意味着有可能写出在桌面电脑和手机中都能使用的程序。
-2007年,Douglas Crockford 发表了名为《JavaScript: The good parts》的演讲,次年由 O'Reilly 出版社出版。这标志着软件行业开始严肃对待 JavaScript 语言,对它的语法开始重新认识,
+2007年,Douglas Crockford 发表了名为《JavaScript: The good parts》的演讲,次年由 O'Reilly 出版社出版。这标志着软件行业开始严肃对待 JavaScript 语言,对它的语法开始重新认识。
2008年,V8 编译器诞生。这是 Google 公司为 Chrome 浏览器而开发的,它的特点是让 JavaScript 的运行变得非常快。它提高了 JavaScript 的性能,推动了语法的改进和标准化,改变外界对 JavaScript 的不佳印象。同时,V8 是开源的,任何人想要一种快速的嵌入式脚本语言,都可以采用 V8,这拓展了 JavaScript 的应用领域。
@@ -181,7 +181,7 @@ JavaScript 伴随着互联网的发展一起发展。互联网周边技术的快
## 参考链接
-- Axel Rauschmayer, [The Past, Present, and Future of JavaScript](http://oreilly.com/javascript/radarreports/past-present-future-javascript.csp)
+- Axel Rauschmayer, [The Past, Present, and Future of JavaScript](https://www.oreilly.com/library/view/the-past-present/9781449343545/)
- John Dalziel, [The race for speed part 4: The future for JavaScript](http://creativejs.com/2013/06/the-race-for-speed-part-4-the-future-for-javascript/)
-- Axel Rauschmayer, [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html)
-- resin.io, [Happy 18th Birthday JavaScript! A look at an unlikely past and bright future](http://resin.io/happy-18th-birthday-javascript/)
+- Axel Rauschmayer, [Basic JavaScript for the impatient programmer](https://www.2ality.com/2013/06/basic-javascript.html)
+- balena.io, [Happy 18th Birthday JavaScript! A look at an unlikely past and bright future](https://www.balena.io/blog/happy-18th-birthday-javascript/)
diff --git a/docs/bom/arraybuffer.md b/docs/bom/arraybuffer.md
index 1ad0e2b..89a5ddd 100644
--- a/docs/bom/arraybuffer.md
+++ b/docs/bom/arraybuffer.md
@@ -164,8 +164,8 @@ FileReader 对象提供四个方法,处理 Blob 对象。Blob 对象作为参
```javascript
// HTML 代码如下
-//
-//
+//
+//
function readfile(f) {
var reader = new FileReader();
reader.readAsText(f);
@@ -210,3 +210,4 @@ function typefile(file) {
};
}
```
+
diff --git a/docs/bom/cookie.md b/docs/bom/cookie.md
index 9c7f53b..e0ae045 100644
--- a/docs/bom/cookie.md
+++ b/docs/bom/cookie.md
@@ -4,13 +4,15 @@
Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。
-Cookie 主要保存状态信息,以下是一些主要用途。
+HTTP 协议不带有状态,有些请求需要区分状态,就通过 Cookie 附带字符串,让服务器返回不一样的回应。举例来说,用户登录以后,服务器往往会在网站上留下一个 Cookie,记录用户编号(比如`id=1234`),以后每次浏览器向服务器请求数据,就会带上这个字符串,服务器从而知道是谁在请求,应该回应什么内容。
-- 对话(session)管理:保存登录、购物车等需要记录的信息。
+Cookie 的目的就是区分用户,以及放置状态信息,它的使用场景主要如下。
+
+- 对话(session)管理:保存登录状态、购物车等需要记录的信息。
- 个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等。
- 追踪用户:记录和分析用户行为。
-Cookie 不是一种理想的客户端储存机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端储存应该使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面。
+Cookie 不是一种理想的客户端存储机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端存储建议使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面。
每个 Cookie 都有以下几方面的元数据。
@@ -20,7 +22,9 @@ Cookie 不是一种理想的客户端储存机制。它的容量很小(4KB)
- 所属域名(默认为当前域名)
- 生效的路径(默认为当前网址)
-举例来说,用户访问网址`www.example.com`,服务器在浏览器写入一个 Cookie。这个 Cookie 的所属域名为`www.example.com`,生效路径为根路径`/`。如果 Cookie 的生效路径设为`/forums`,那么这个 Cookie 只有在访问`www.example.com/forums`及其子路径时才有效。以后,浏览器访问某个路径之前,就会找出对该域名和路径有效,并且还没有到期的 Cookie,一起发送给服务器。
+举例来说,用户访问网址`www.example.com`,服务器在浏览器写入一个 Cookie。这个 Cookie 的所属域名为`www.example.com`,生效路径为根路径`/`。
+
+如果 Cookie 的生效路径设为`/forums`,那么这个 Cookie 只有在访问`www.example.com/forums`及其子路径时才有效。以后,浏览器访问某个路径之前,就会找出对该域名和路径有效,并且还没有到期的 Cookie,一起发送给服务器。
用户可以设置浏览器不接受 Cookie,也可以设置不向服务器发送 Cookie。`window.navigator.cookieEnabled`属性返回一个布尔值,表示浏览器是否打开 Cookie 功能。
@@ -34,9 +38,17 @@ window.navigator.cookieEnabled // true
document.cookie // "id=foo;key=bar"
```
-不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过4KB。超过限制以后,Cookie 将被忽略,不会被设置。
+不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过 4KB。超过限制以后,Cookie 将被忽略,不会被设置。
+
+Cookie 是按照域名区分的,`foo.com`只能读取自己放置的 Cookie,无法读取其他网站(比如`bar.com`)放置的 Cookie。一般情况下,一级域名也不能读取二级域名留下的 Cookie,比如`mydomain.com`不能读取`subdomain.mydomain.com`设置的 Cookie。但是有一个例外,设置 Cookie 的时候(不管是一级域名设置的,还是二级域名设置的),明确将`domain`属性设为一级域名,则这个域名下面的各级域名可以共享这个 Cookie。
+
+```http
+Set-Cookie: name=value; domain=mydomain.com
+```
+
+上面示例中,设置 Cookie 时,`domain`属性设为`mydomain.com`,那么各级的子域名和一级域名都可以读取这个 Cookie。
-浏览器的同源政策规定,两个网址只要域名相同,就可以共享 Cookie(参见《同源政策》一章)。注意,这里不要求协议相同。也就是说,`http://example.com`设置的 Cookie,可以被`https://example.com`读取。
+注意,区分 Cookie 时不考虑协议和端口。也就是说,`http://example.com`设置的 Cookie,可以被`https://example.com`或`http://example.com:8080`读取。
## Cookie 与 HTTP 协议
@@ -163,9 +175,19 @@ Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
### Domain,Path
-`Domain`属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前域名,这时子域名将不会附带这个 Cookie。比如,`example.com`不设置 Cookie 的`domain`属性,那么`sub.example.com`将不会附带这个 Cookie。如果指定了`domain`属性,那么子域名也会附带这个 Cookie。如果服务器指定的域名不属于当前域名,浏览器会拒绝这个 Cookie。
+`Domain`属性指定 Cookie 属于哪个域名,以后浏览器向服务器发送 HTTP 请求时,通过这个属性判断是否要附带某个 Cookie。
-`Path`属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,`Path`属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,`PATH`属性是`/`,那么请求`/docs`路径也会包含该 Cookie。当然,前提是域名必须一致。
+服务器设定 Cookie 时,如果没有指定 Domain 属性,浏览器会默认将其设为浏览器的当前域名。如果当前域名是一个 IP 地址,则不得设置 Domain 属性。
+
+如果指定 Domain 属性,需要遵守下面规则:Domain 属性只能是当前域名或者当前域名的上级域名,但设为上级域名时,不能设为顶级域名或公共域名。(顶级域名指的是 .com、.net 这样的域名,公共域名指的是开放给外部用户设置子域名的域名,比如 github.io。)如果不符合上面这条规则,浏览器会拒绝设置这个 Cookie。
+
+举例来说,当前域名为`x.y.z.com`,那么 Domain 属性可以设为`x.y.z.com`,或者`y.z.com`,或者`z.com`,但不能设为`foo.x.y.z.com`,或者`another.domain.com`。
+
+另一个例子是,当前域名为`wangdoc.github.io`,则 Domain 属性只能设为`wangdoc.github.io`,不能设为`github.io`,因为后者是一个公共域名。
+
+浏览器发送 Cookie 时,Domain 属性必须与当前域名一致,或者是当前域名的上级域名(公共域名除外)。比如,Domain 属性是`y.z.com`,那么适用于`y.z.com`、`x.y.z.com`、`foo.x.y.z.com`等域名。再比如,Domain 属性是公共域名`github.io`,那么只适用于`github.io`这个域名本身,不适用于它的子域名`wangdoc.github.io`。
+
+`Path`属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,`Path`属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,`Path`属性是`/`,那么请求`/docs`路径也会包含该 Cookie。当然,前提是 Domain 属性必须符合条件。
### Secure,HttpOnly
@@ -197,7 +219,7 @@ Set-Cookie:id=a3fWa;
```
-用户一旦被诱骗发送这个表单,银行网站就会收到带有正确 Cookie 的请求。为了防止这种攻击,表单一般都带有一个随机 token,告诉服务器这是真实请求。
+用户一旦被诱骗发送这个表单,银行网站就会收到带有正确 Cookie 的请求。为了防止这种攻击,官网的表单一般都带有一个随机 token,官网服务器通过验证这个随机 token,确认是否为真实请求。
```html
```
-这种第三方网站引导发出的 Cookie,就称为第三方 Cookie。它除了用于 CSRF 攻击,还可以用于用户追踪。比如,Facebook 在第三方网站插入一张看不见的图片。
+这种第三方网站引导而附带发送的 Cookie,就称为第三方 Cookie。它除了用于 CSRF 攻击,还可以用于用户追踪。比如,Facebook 在第三方网站插入一张看不见的图片。
```html
@@ -320,7 +342,7 @@ document.cookie = "foo=bar; expires=Fri, 31 Dec 2020 23:59:59 GMT";
各个属性的写入注意点如下。
- `path`属性必须为绝对路径,默认为当前路径。
-- `domain`属性值必须是当前发送 Cookie 的域名的一部分。比如,当前域名是`example.com`,就不能将其设为`foo.com`。该属性默认为当前的一级域名(不含二级域名)。
+- `domain`属性值必须是当前发送 Cookie 的域名的一部分。比如,当前域名是`example.com`,就不能将其设为`foo.com`。该属性默认为当前的一级域名(不含二级域名)。如果显式设置该属性,则该域名的任意子域名也可以读取 Cookie。
- `max-age`属性的值为秒数。
- `expires`属性的值为 UTC 格式,可以使用`Date.prototype.toUTCString()`进行日期格式转换。
@@ -330,9 +352,11 @@ document.cookie = "foo=bar; expires=Fri, 31 Dec 2020 23:59:59 GMT";
document.cookie = 'fontSize=14; '
+ 'expires=' + someDate.toGMTString() + '; '
+ 'path=/subdirectory; '
- + 'domain=*.example.com';
+ + 'domain=example.com';
```
+注意,上面的`domain`属性,以前的写法是`.example.com`,表示子域名也可以读取该 Cookie,新的写法可以省略前面的点。
+
Cookie 的属性一旦设置完成,就没有办法读取这些属性的值。
删除一个现存 Cookie 的唯一方法,是设置它的`expires`属性为一个过去的日期。
diff --git a/docs/bom/cors.md b/docs/bom/cors.md
index 368ea0c..566e1f0 100644
--- a/docs/bom/cors.md
+++ b/docs/bom/cors.md
@@ -1,12 +1,12 @@
# CORS 通信
-CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出`XMLHttpRequest`请求,从而克服了 AJAX 只能同源使用的限制。
+CORS 是一个 W3C 标准,全称是“跨源资源共享”(Cross-origin resource sharing),或者通俗地称为“跨域资源共享”。它允许浏览器向跨源的服务器,发出`XMLHttpRequest`请求,从而克服了 AJAX 只能同源使用的限制。
## 简介
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。
-整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨域通信。
+整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
## 两种请求
@@ -30,7 +30,7 @@ CORS 请求分成两类:简单请求(simple request)和非简单请求(n
凡是不同时满足上面两个条件,就属于非简单请求。一句话,简单请求就是简单的 HTTP 方法与简单的 HTTP 头信息的结合。
-这样划分的原因是,表单在历史上一直可以跨域发出请求。简单请求就是表单请求,浏览器沿袭了传统的处理方式,不把行为复杂化,否则开发者可能转而使用表单,规避 CORS 的限制。对于非简单请求,浏览器会采用新的处理方式。
+这样划分的原因是,表单在历史上一直可以跨源发出请求。简单请求就是表单请求,浏览器沿袭了传统的处理方式,不把行为复杂化,否则开发者可能转而使用表单,规避 CORS 的限制。对于非简单请求,浏览器会采用新的处理方式。
## 简单请求
@@ -38,7 +38,7 @@ CORS 请求分成两类:简单请求(simple request)和非简单请求(n
对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是在头信息之中,增加一个`Origin`字段。
-下面是一个例子,浏览器发现这次跨域 AJAX 请求是简单请求,就自动在头信息之中,添加一个`Origin`字段。
+下面是一个例子,浏览器发现这次跨源 AJAX 请求是简单请求,就自动在头信息之中,添加一个`Origin`字段。
```http
GET /cors HTTP/1.1
@@ -99,7 +99,7 @@ xhr.withCredentials = true;
xhr.withCredentials = false;
```
-需要注意的是,如果服务器要求浏览器发送 Cookie,`Access-Control-Allow-Origin`就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨域)原网页代码中的`document.cookie`也无法读取服务器域名下的 Cookie。
+需要注意的是,如果服务器要求浏览器发送 Cookie,`Access-Control-Allow-Origin`就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨源)原网页代码中的`document.cookie`也无法读取服务器域名下的 Cookie。
## 非简单请求
@@ -107,7 +107,7 @@ xhr.withCredentials = false;
非简单请求是那种对服务器提出特殊要求的请求,比如请求方法是`PUT`或`DELETE`,或者`Content-Type`字段的类型是`application/json`。
-非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为“预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的`XMLHttpRequest`请求,否则就报错。这是为了防止这些新增的请求,对传统的没有 CORS 支持的服务器形成压力,给服务器一个提前拒绝的机会,这样可以防止服务器收到大量`DELETE`和`PUT`请求,这些传统的表单不可能跨域发出的请求。
+非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为“预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的`XMLHttpRequest`请求,否则就报错。这是为了防止这些新增的请求,对传统的没有 CORS 支持的服务器形成压力,给服务器一个提前拒绝的机会,这样可以防止服务器收到大量`DELETE`和`PUT`请求,这些传统的表单不可能跨源发出的请求。
下面是一段浏览器的 JavaScript 脚本。
@@ -162,7 +162,6 @@ Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
-Content-Type: text/plain
```
上面的 HTTP 回应中,关键的是`Access-Control-Allow-Origin`字段,表示`http://api.bob.com`可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
@@ -200,7 +199,7 @@ Access-Control-Max-Age: 1728000
**(1)`Access-Control-Allow-Methods`**
-该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次“预检”请求。
+该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨源请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次“预检”请求。
**(2)`Access-Control-Allow-Headers`**
diff --git a/docs/bom/engine.md b/docs/bom/engine.md
index 86e7f53..8c0761e 100644
--- a/docs/bom/engine.md
+++ b/docs/bom/engine.md
@@ -436,4 +436,4 @@ JavaScript 是一种解释型语言,也就是说,它不需要编译,由解
- Axel Rauschmayer, [ECMAScript 6 promises (1/2): foundations](http://www.2ality.com/2014/09/es6-promises-foundations.html)
- Daniel Imms, [async vs defer attributes](http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html)
- Craig Buckler, [Load Non-blocking JavaScript with HTML5 Async and Defer](http://www.sitepoint.com/non-blocking-async-defer/)
-- Domenico De Felice, [How browsers work](http://domenicodefelice.blogspot.sg/2015/08/how-browsers-work.html?t=2)
+- Domenico De Felice, [How browsers work](https://domenicodefelice.blogspot.com/2015/08/how-browsers-work.html)
diff --git a/docs/bom/form.md b/docs/bom/form.md
index c4bb42b..7b71500 100644
--- a/docs/bom/form.md
+++ b/docs/bom/form.md
@@ -80,15 +80,15 @@ formElement.reset()
### 概述
-表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。但是有时候,我们希望通过脚本完成过程,构造和编辑表单键值对,然后通过`XMLHttpRequest.send()`方法发送。浏览器原生提供了 FormData 对象来完成这项工作。
+表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。但是有时候,我们希望通过脚本完成这个过程,构造或编辑表单的键值对,然后通过脚本发送给服务器。浏览器原生提供了 FormData 对象来完成这项工作。
-FormData 首先是一个构造函数,用来生成实例。
+`FormData()`首先是一个构造函数,用来生成表单的实例。
```javascript
var formdata = new FormData(form);
```
-`FormData()`构造函数的参数是一个表单元素,这个参数是可选的。如果省略参数,就表示一个空的表单,否则就会处理表单元素里面的键值对。
+`FormData()`构造函数的参数是一个 DOM 的表单元素,构造函数会自动处理表单的键值对。这个参数是可选的,如果省略该参数,就表示一个空的表单。
下面是一个表单。
@@ -110,7 +110,7 @@ var formdata = new FormData(form);
```
-我们用 FormData 对象处理上面这个表单。
+我们用`FormData()`处理上面这个表单。
```javascript
var myForm = document.getElementById('myForm');
diff --git a/docs/bom/history.md b/docs/bom/history.md
index 764b4bd..174f268 100644
--- a/docs/bom/history.md
+++ b/docs/bom/history.md
@@ -62,7 +62,7 @@ history.go(0); // 刷新当前页面
注意,移动到以前访问过的页面时,页面通常是从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。
-### History.pushState(),
+### History.pushState()
`History.pushState()`方法用于在历史中添加一条记录。
diff --git a/docs/bom/indexeddb.md b/docs/bom/indexeddb.md
index 83b5a86..7472264 100644
--- a/docs/bom/indexeddb.md
+++ b/docs/bom/indexeddb.md
@@ -932,15 +932,14 @@ objectStore.openCursor(null, 'next').onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
var listItem = document.createElement('li');
- listItem.innerHTML = cursor.value.albumTitle + ', ' + cursor.value.year;
- list.appendChild(listItem);
+ listItem.innerHTML = cursor.value.albumTitle + ', ' + cursor.value.year;
+ list.appendChild(listItem);
- console.log(cursor.source);
- cursor.continue();
- } else {
- console.log('Entries all displayed.');
- }
- };
+ console.log(cursor.source);
+ cursor.continue();
+ } else {
+ console.log('Entries all displayed.');
+ }
};
```
diff --git a/docs/bom/location.md b/docs/bom/location.md
index 4ea667e..7233095 100644
--- a/docs/bom/location.md
+++ b/docs/bom/location.md
@@ -173,11 +173,11 @@ decodeURIComponent('%E6%98%A5%E8%8A%82')
## URL 接口
-`URL`接口是一个构造函数,浏览器原生提供,可以用来构造、解析和编码 URL。一般情况下,通过`window.URL`可以拿到这个构造函数。
+浏览器原生提供`URL()`接口,它是一个构造函数,用来构造、解析和编码 URL。一般情况下,通过`window.URL`可以拿到这个构造函数。
### 构造函数
-`URL`作为构造函数,可以生成 URL 实例。它接受一个表示 URL 的字符串作为参数。如果参数不是合法的 URL,会报错。
+`URL()`作为构造函数,可以生成 URL 实例。它接受一个表示 URL 的字符串作为参数。如果参数不是合法的 URL,会报错。
```javascript
var url = new URL('http://www.example.com/index.html');
@@ -185,7 +185,9 @@ url.href
// "http://www.example.com/index.html"
```
-如果参数是另一个 URL 实例,构造函数会自动读取该实例的`href`属性,作为实际参数。
+上面示例生成了一个 URL 实例,用来代表指定的网址。
+
+除了字符串,`URL()`的参数也可以是另一个 URL 实例。这时,`URL()`会自动读取该实例的`href`属性,作为实际参数。
如果 URL 字符串是一个相对路径,那么需要表示绝对路径的第二个参数,作为计算基准。
@@ -251,7 +253,7 @@ url.username
// "user"
```
-这些属性里面,只有`origin`属性是只读的,其他属性都可写。
+这些属性里面,只有`origin`属性是只读的,其他属性都可写,并且会立即生效。
```javascript
var url = new URL('http://example.com/index.html#part1');
@@ -367,7 +369,7 @@ fetch('https://example.com/api', {
上面代码中,`fetch`命令向服务器发送命令时,可以直接使用`URLSearchParams`实例。
-`URLSearchParams`可以与`URL`接口结合使用。
+`URLSearchParams`可以与`URL()`接口结合使用。
```javascript
var url = new URL(window.location);
@@ -479,7 +481,7 @@ params.toString() // "foo=3"
```javascript
// URL: https://example.com?version=1.0
var params = new URLSearchParams(location.search.slice(1));
-params.set('version', 2.0);
+params.set('version', '2.0');
window.history.replaceState({}, '', location.pathname + `?` + params);
// URL: https://example.com?version=2.0
diff --git a/docs/bom/navigator.md b/docs/bom/navigator.md
index f703551..18476a7 100644
--- a/docs/bom/navigator.md
+++ b/docs/bom/navigator.md
@@ -6,7 +6,7 @@
### Navigator.userAgent
-`navigator.userAgent`属性返回浏览器的 User Agent 字符串,表示浏览器的厂商和版本信息。
+`navigator.userAgent`属性返回浏览器的 User Agent 字符串,表示用户设备信息,包含了浏览器的厂商、版本、操作系统等信息。
下面是 Chrome 浏览器的`userAgent`。
@@ -22,7 +22,7 @@ navigator.userAgent
```javascript
var ua = navigator.userAgent.toLowerCase();
-if (/mobi/i.test(ua)) {
+if (/mobi/.test(ua)) {
// 手机浏览器
} else {
// 非手机浏览器
@@ -32,7 +32,7 @@ if (/mobi/i.test(ua)) {
如果想要识别所有移动设备的浏览器,可以测试更多的特征字符串。
```javascript
-/mobi|android|touch|mini/i.test(ua)
+/mobi|android|touch|mini/.test(ua)
```
### Navigator.plugins
@@ -106,7 +106,7 @@ Geolocation 对象提供下面三个方法。
### Navigator.cookieEnabled
-`Navigator.cookieEnabled`属性返回一个布尔值,表示浏览器的 Cookie 功能是否打开。
+`navigator.cookieEnabled`属性返回一个布尔值,表示浏览器的 Cookie 功能是否打开。
```javascript
navigator.cookieEnabled // true
@@ -118,7 +118,7 @@ navigator.cookieEnabled // true
### Navigator.javaEnabled()
-`Navigator.javaEnabled()`方法返回一个布尔值,表示浏览器是否能运行 Java Applet 小程序。
+`navigator.javaEnabled()`方法返回一个布尔值,表示浏览器是否能运行 Java Applet 小程序。
```javascript
navigator.javaEnabled() // false
@@ -128,6 +128,73 @@ navigator.javaEnabled() // false
`Navigator.sendBeacon()`方法用于向服务器异步发送数据,详见《XMLHttpRequest 对象》一章。
+## Navigator 的实验性属性
+
+Navigator 对象有一些实验性属性,在部分浏览器可用。
+
+### Navigator.deviceMemory
+
+`navigator.deviceMemory`属性返回当前计算机的内存数量(单位为 GB)。该属性只读,只在 HTTPS 环境下可用。
+
+它的返回值是一个近似值,四舍五入到最接近的2的幂,通常是 0.25、0.5、1、2、4、8。实际内存超过 8GB,也返回`8`。
+
+```javascript
+if (navigator.deviceMemory > 1) {
+ await import('./costly-module.js');
+}
+```
+
+上面示例中,只有当前内存大于 1GB,才加载大型的脚本。
+
+### Navigator.hardwareConcurrency
+
+`navigator.hardwareConcurrency`属性返回用户计算机上可用的逻辑处理器的数量。该属性只读。
+
+现代计算机的 CPU 有多个物理核心,每个物理核心有时支持一次运行多个线程。因此,四核 CPU 可以提供八个逻辑处理器核心。
+
+```javascript
+if (navigator.hardwareConcurrency > 4) {
+ await import('./costly-module.js');
+}
+```
+
+上面示例中,可用的逻辑处理器大于4,才会加载大型脚本。
+
+该属性通过用于创建 Web Worker,每个可用的逻辑处理器都创建一个 Worker。
+
+```javascript
+let workerList = [];
+
+for (let i = 0; i < window.navigator.hardwareConcurrency; i++) {
+ let newWorker = {
+ worker: new Worker('cpuworker.js'),
+ inUse: false
+ };
+ workerList.push(newWorker);
+}
+```
+
+上面示例中,有多少个可用的逻辑处理器,就创建多少个 Web Worker。
+
+### Navigator.connection
+
+`navigator.connection`属性返回一个对象,包含当前网络连接的相关信息。
+
+- downlink:有效带宽估计值(单位:兆比特/秒,Mbps),四舍五入到每秒 25KB 的最接近倍数。
+- downlinkMax:当前连接的最大下行链路速度(单位:兆比特每秒,Mbps)。
+- effectiveType:返回连接的等效类型,可能的值为`slow-2g`、`2g`、`3g`、`4g`。
+- rtt:当前连接的估计有效往返时间,四舍五入到最接近的25毫秒的倍数。
+- saveData:用户是否设置了浏览器的减少数据使用量选项(比如不加载图片),返回`true`或者`false`。
+- type:当前连接的介质类型,可能的值为`bluetooth`、`cellular`、`ethernet`、`none`、`wifi`、`wimax`、`other`、`unknown`。
+
+```javascript
+if (navigator.connection.effectiveType === '4g') {
+ await import('./costly-module.js');
+}
+```
+
+上面示例中,如果网络连接是 4G,则加载大型脚本。
+
## Screen 对象
Screen 对象表示当前窗口所在的屏幕,提供显示设备的信息。`window.screen`属性指向这个对象。
@@ -140,7 +207,7 @@ Screen 对象表示当前窗口所在的屏幕,提供显示设备的信息。`
- `Screen.availWidth`:浏览器窗口可用的屏幕宽度(单位像素)。
- `Screen.pixelDepth`:整数,表示屏幕的色彩位数,比如`24`表示屏幕提供24位色彩。
- `Screen.colorDepth`:`Screen.pixelDepth`的别名。严格地说,colorDepth 表示应用程序的颜色深度,pixelDepth 表示屏幕的颜色深度,绝大多数情况下,它们都是同一件事。
-- `Screen.orientation`:返回一个对象,表示屏幕的方向。该对象的`type`属性是一个字符串,表示屏幕的具体方向,`landscape-primary`表示横放,`landscape-secondary`表示颠倒的横放,`portrait-primary`表示竖放,`portrait-secondary`。
+- `Screen.orientation`:返回一个对象,表示屏幕的方向。该对象的`type`属性是一个字符串,表示屏幕的具体方向,`landscape-primary`表示横放,`landscape-secondary`表示颠倒的横放,`portrait-primary`表示竖放,`portrait-secondary`表示颠倒的竖放。
下面是`Screen.orientation`的例子。
@@ -166,3 +233,4 @@ if ((screen.width <= 800) && (screen.height <= 600)) {
window.location.replace('wide.html');
}
```
+
diff --git a/docs/bom/same-origin.md b/docs/bom/same-origin.md
index 553dc39..4a11a0b 100644
--- a/docs/bom/same-origin.md
+++ b/docs/bom/same-origin.md
@@ -89,10 +89,10 @@ var allCookie = document.cookie;
注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexedDB 无法通过这种方法,规避同源政策,而要使用下文介绍 PostMessage API。
-另外,服务器也可以在设置 Cookie 的时候,指定 Cookie 的所属域名为一级域名,比如`.example.com`。
+另外,服务器也可以在设置 Cookie 的时候,指定 Cookie 的所属域名为一级域名,比如`example.com`。
```http
-Set-Cookie: key=value; domain=.example.com; path=/
+Set-Cookie: key=value; domain=example.com; path=/
```
这样的话,二级域名和三级域名不用做任何设置,都可以读取这个 Cookie。
@@ -194,7 +194,7 @@ window.addEventListener('message', function (e) {
`message`事件的参数是事件对象`event`,提供以下三个属性。
> - `event.source`:发送消息的窗口
-> - `event.origin`: 消息发向的网址
+> - `event.origin`: 消息发送者的源(origin),即协议、域名、端口。
> - `event.data`: 消息内容
下面的例子是,子窗口通过`event.source`属性引用父窗口,然后发送消息。
@@ -208,7 +208,7 @@ function receiveMessage(event) {
上面代码有几个地方需要注意。首先,`receiveMessage`函数里面没有过滤信息的来源,任意网址发来的信息都会被处理。其次,`postMessage`方法中指定的目标窗口的网址是一个星号,表示该信息可以向任意网址发送。通常来说,这两种做法是不推荐的,因为不够安全,可能会被恶意利用。
-`event.origin`属性可以过滤不是发给本窗口的消息。
+`event.origin`属性可以过滤非许可地址发来的消息。
```javascript
window.addEventListener('message', receiveMessage);
diff --git a/docs/bom/storage.md b/docs/bom/storage.md
index 0d5723e..2eb48d2 100644
--- a/docs/bom/storage.md
+++ b/docs/bom/storage.md
@@ -86,7 +86,7 @@ window.localStorage.clear()
### Storage.key()
-`Storage.key()`接受一个整数作为参数(从零开始),返回该位置对应的键值。
+`Storage.key()`方法接受一个整数作为参数(从零开始),返回该位置对应的键名。
```javascript
window.sessionStorage.setItem('key', 'value');
diff --git a/docs/bom/webworker.md b/docs/bom/webworker.md
index 7a7f38a..d005a8b 100644
--- a/docs/bom/webworker.md
+++ b/docs/bom/webworker.md
@@ -142,11 +142,11 @@ importScripts('script1.js', 'script2.js');
主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的`error`事件。
```javascript
-worker.onerror(function (event) {
- console.log([
+worker.onerror = function (event) {
+ console.log(
'ERROR: Line ', event.lineno, ' in ', event.filename, ': ', event.message
- ].join(''));
-});
+ );
+};
// 或者
worker.addEventListener('error', function (event) {
@@ -381,7 +381,7 @@ Worker 线程有一些自己的全局属性和方法。
- self.onmessage:指定`message`事件的监听函数。
- self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
- self.close():关闭 Worker 线程。
-- self.postMessage():向产生这个 Worker 线程发送消息。
+- self.postMessage():向产生这个 Worker 的线程发送消息。
- self.importScripts():加载 JS 脚本。
(完)
diff --git a/docs/bom/window.md b/docs/bom/window.md
index f5eea3b..a873fae 100644
--- a/docs/bom/window.md
+++ b/docs/bom/window.md
@@ -154,7 +154,7 @@ if (window.parent !== window.top) {
`window.innerHeight`和`window.innerWidth`属性,返回网页在当前窗口中可见部分的高度和宽度,即“视口”(viewport)的大小(单位像素)。这两个属性只读。
-用户放大网页的时候(比如将网页从100%的大小放大为200%),这两个属性会变小。因为这时网页的像素大小不变(比如宽度还是960像素),只是每个像素占据的屏幕空间变大了,因为可见部分(视口)就变小了。
+用户放大网页的时候(比如将网页从100%的大小放大为200%),这两个属性会变小。因为这时网页的像素大小不变(比如宽度还是960像素),只是每个像素占据的屏幕空间变大了,因此可见部分(视口)就变小了。
注意,这两个属性值包括滚动条的高度和宽度。
@@ -579,7 +579,7 @@ var start = null;
function step(timestamp) {
if (!start) start = timestamp;
var progress = timestamp - start;
- // 元素不断向左移,最大不超过200像素
+ // 元素不断向右移,最大不超过200像素
element.style.left = Math.min(progress / 10, 200) + 'px';
// 如果距离第一次执行不超过 2000 毫秒,
// 就继续执行动画
@@ -599,7 +599,7 @@ window.requestAnimationFrame(step);
它跟`window.requestAnimationFrame()`的区别在于,后者指定回调函数在下一次浏览器重排时执行,问题在于下一次重排时,系统资源未必空闲,不一定能保证在16毫秒之内完成;`window.requestIdleCallback()`可以保证回调函数在系统资源空闲时执行。
-该方法接受一个回调函数和一个配置对象作为参数。配置对象可以指定一个推迟执行的最长时间,如果过了这个时间,回调函数不管系统资源有无空虚,都会执行。
+该方法接受一个回调函数和一个配置对象作为参数。配置对象可以指定一个推迟执行的最长时间,如果过了这个时间,回调函数不管系统资源有无空闲,都会执行。
```javascript
window.requestIdleCallback(callback[, options])
diff --git a/docs/bom/xmlhttprequest.md b/docs/bom/xmlhttprequest.md
index be0ee13..66f662c 100644
--- a/docs/bom/xmlhttprequest.md
+++ b/docs/bom/xmlhttprequest.md
@@ -143,7 +143,7 @@ xhr.onreadystatechange = function () {
### XMLHttpRequest.responseType
-`XMLHttpRequest.responseType`属性是一个字符串,表示服务器返回数据的类型。这个属性是可写的,可以在调用`open()`方法之后、调用`send()`方法之前,设置这个属性的值,告诉服务器返回指定类型的数据。如果`responseType`设为空字符串,就等同于默认值`text`。
+`XMLHttpRequest.responseType`属性是一个字符串,表示服务器返回数据的类型。这个属性是可写的,可以在调用`open()`方法之后、调用`send()`方法之前,设置这个属性的值,告诉浏览器如何解读返回的数据。如果`responseType`设为空字符串,就等同于默认值`text`。
`XMLHttpRequest.responseType`属性可以等于以下值。
@@ -743,7 +743,7 @@ window.addEventListener('unload', function(event) {
上面代码中,强制执行了一次双重循环,拖长了`unload`事件的执行时间,导致异步 AJAX 能够发送成功。
-类似的还可以使用`setTimeout`。下面是追踪用户点击的例子。
+类似的还可以使用`setTimeout()`。下面是追踪用户点击的例子。
```javascript
// HTML 代码如下
@@ -768,7 +768,7 @@ theLink.addEventListener('click', function (event) {
});
```
-上面代码使用`setTimeout`,拖延了350毫秒,才让页面跳转,因此使得异步 AJAX 有时间发出。
+上面代码使用`setTimeout()`,拖延了350毫秒,才让页面跳转,因此使得异步 AJAX 有时间发出。
这些做法的共同问题是,卸载的时间被硬生生拖长了,后面页面的加载被推迟了,用户体验不好。
@@ -778,11 +778,13 @@ theLink.addEventListener('click', function (event) {
window.addEventListener('unload', logData, false);
function logData() {
- navigator.sendBeacon('/log', analyticsData);
+ navigator.sendBeacon('/log', JSON.stringify({
+ some: "data"
+ }));
}
```
-`Navigator.sendBeacon`方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等等)。
+`Navigator.sendBeacon()`方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等等)。
```javascript
navigator.sendBeacon(url, data)
@@ -806,3 +808,16 @@ function analytics(state) {
navigator.sendBeacon(URL, data);
}
```
+
+该方法不允许自定义 HTTP 标头,为了以“application/json”的形式发送数据,可以使用 Blob 对象。
+
+```javascript
+const blob = new Blob(
+ [ JSON.stringify({ some: "data" }) ],
+ { type: 'application/json; charset=UTF-8' }
+);
+navigator.sendBeacon('/log', blob));
+```
+
+这个方法的优先级较低,不会占用页面资源。一般是在浏览器空闲的时候,才会发送。
+
diff --git a/docs/dom/css.md b/docs/dom/css.md
index a29de76..b4f1a2d 100644
--- a/docs/dom/css.md
+++ b/docs/dom/css.md
@@ -292,7 +292,7 @@ CSS.supports('display: table-cell;') // false
行内样式(inline style)具有最高的优先级,改变行内样式,通常会立即反映出来。但是,网页元素最终的样式是综合各种规则计算出来的。因此,如果想得到元素实际的样式,只读取行内样式是不够的,需要得到浏览器最终计算出来的样式规则。
-`window.getComputedStyle`方法,就用来返回浏览器计算后得到的最终规则。它接受一个节点对象作为参数,返回一个 CSSStyleDeclaration 实例,包含了指定节点的最终样式信息。所谓“最终样式信息”,指的是各种 CSS 规则叠加后的结果。
+`window.getComputedStyle()`方法,就用来返回浏览器计算后得到的最终规则。它接受一个节点对象作为参数,返回一个 CSSStyleDeclaration 实例,包含了指定节点的最终样式信息。所谓“最终样式信息”,指的是各种 CSS 规则叠加后的结果。
```javascript
var div = document.querySelector('div');
@@ -328,7 +328,7 @@ var height = styleObj.getPropertyValue('height');
- CSSStyleDeclaration 实例返回的 CSS 值都是绝对单位。比如,长度都是像素单位(返回值包括`px`后缀),颜色是`rgb(#, #, #)`或`rgba(#, #, #, #)`格式。
- CSS 规则的简写形式无效。比如,想读取`margin`属性的值,不能直接读,只能读`marginLeft`、`marginTop`等属性;再比如,`font`属性也是不能直接读的,只能读`font-size`等单个属性。
- 如果读取 CSS 原始的属性名,要用方括号运算符,比如`styleObj['z-index']`;如果读取骆驼拼写法的 CSS 属性名,可以直接读取`styleObj.zIndex`。
-- 该方法返回的 CSSStyleDeclaration 实例的`cssText`属性无效,返回`undefined`。
+- 该方法返回的 CSSStyleDeclaration 实例的`cssText`属性总是返回空字符串。
## CSS 伪元素
@@ -401,9 +401,9 @@ myStyleSheet instanceof StyleSheet // true
注意,`disabled`属性只能在 JavaScript 脚本中设置,不能在 HTML 语句中设置。
-**(2)Stylesheet.href**
+**(2)StyleSheet.href**
-`Stylesheet.href`返回样式表的网址。对于内嵌样式表,该属性返回`null`。该属性只读。
+`StyleSheet.href`返回样式表的网址。对于内嵌样式表,该属性返回`null`。该属性只读。
```javascript
document.styleSheets[0].href
@@ -749,7 +749,7 @@ styleSheet.cssRules[0].conditionText
### 基本用法
-`window.matchMedia`方法用来将 CSS 的[`MediaQuery`](https://developer.mozilla.org/en-US/docs/DOM/Using_media_queries_from_code)条件语句,转换成一个 MediaQueryList 实例。
+`window.matchMedia()`方法用来将 CSS 的[`Media Query`](https://developer.mozilla.org/en-US/docs/DOM/Using_media_queries_from_code)条件语句,转换成一个 MediaQueryList 实例。
```javascript
var mdl = window.matchMedia('(min-width: 400px)');
diff --git a/docs/dom/document.md b/docs/dom/document.md
index 46da3e9..7af7f3a 100644
--- a/docs/dom/document.md
+++ b/docs/dom/document.md
@@ -69,7 +69,10 @@ document.scrollingElement.scrollTop = 0;
`document.fullscreenElement`属性返回当前以全屏状态展示的 DOM 元素。如果不是全屏状态,该属性返回`null`。
```javascript
-if (document.fullscreenElement.nodeName == 'VIDEO') {
+if (
+ document.fullscreenElement &&
+ document.fullscreenElement.nodeName == 'VIDEO'
+) {
console.log('全屏播放视频');
}
```
@@ -145,11 +148,11 @@ if (scripts.length !== 0 ) {
**(6)document.styleSheets**
-`document.styleSheets`属性返回文档内嵌或引入的样式表集合,详细介绍请看《CSS 对象模型》一章。
+`document.styleSheets`属性返回网页内嵌或引入的 CSS 样式表集合,详细介绍请看《CSS 操作》一章。
**(7)小结**
-除了`document.styleSheets`,以上的集合属性返回的都是`HTMLCollection`实例。
+除了`document.styleSheets`属性,以上的其他集合属性返回的都是`HTMLCollection`实例。`document.styleSheets`属性返回的是`StyleSheetList`实例。
```javascript
document.links instanceof HTMLCollection // true
@@ -159,7 +162,7 @@ document.embeds instanceof HTMLCollection // true
document.scripts instanceof HTMLCollection // true
```
-`HTMLCollection`实例是类似数组的对象,所以这些属性都有`length`属性,都可以使用方括号运算符引用成员。如果成员有`id`或`name`属性,还可以用这两个属性的值,在`HTMLCollection`实例上引用到这个成员。
+`HTMLCollection`实例是类似数组的对象,所以上面这些属性都有`length`属性,都可以使用方括号运算符引用成员。如果成员有`id`或`name`属性,还可以用这两个属性的值,在`HTMLCollection`实例上引用到这个成员。
```javascript
// HTML 代码如下
@@ -363,7 +366,7 @@ document.replaceChild(
);
```
-上面代码中,第一步生成一个新的 HTML 文档`doc`,然后用它的根元素`document.documentElement`替换掉`document.documentElement`。这会使得当前文档的内容全部消失,变成`hello world`。
+上面代码中,第一步生成一个新的 HTML 文档`doc`,然后用它的根元素`doc.documentElement`替换掉`document.documentElement`。这会使得当前文档的内容全部消失,变成`hello world`。
## 方法
@@ -716,7 +719,7 @@ var element = document.getElementById('ul');
element.appendChild(docfrag);
```
-上面代码中,文档片断`docfrag`包含四个``节点,这些子节点被一次性插入了当前文档。
+上面代码中,文档片段`docfrag`包含四个``节点,这些子节点被一次性插入了当前文档。
### document.createEvent()
diff --git a/docs/dom/element.md b/docs/dom/element.md
index b569f31..6a73887 100644
--- a/docs/dom/element.md
+++ b/docs/dom/element.md
@@ -89,7 +89,7 @@ document.documentElement.lang // "en"
**(1)Element.hidden**
-`Element.hidden`属性返回一个布尔值,表示当前元素的`hidden`属性,用来控制当前元素是否可见。该属性可读写。
+`Element.hidden`属性返回一个布尔值,表示当前 HTML 元素的`hidden`属性的值。该属性可读写,用来控制当前元素是否可见。
```javascript
var btn = document.getElementById('btn');
@@ -100,9 +100,9 @@ btn.addEventListener('click', function () {
}, false);
```
-注意,该属性与 CSS 设置是互相独立的。CSS 对这个元素可见性的设置,`Element.hidden`并不能反映出来。也就是说,这个属性并不能用来判断当前元素的实际可见性。
+注意,该属性与 CSS 设置是互相独立的。CSS 对当前元素可见性的设置,`Element.hidden`并不能反映出来。也就是说,这个属性并不能用来判断当前元素的实际可见性。
-CSS 的设置高于`Element.hidden`。如果 CSS 指定了该元素不可见(`display: none`)或可见(`display: hidden`),那么`Element.hidden`并不能改变该元素实际的可见性。换言之,这个属性只在 CSS 没有明确设定当前元素的可见性时才有效。
+CSS 设置的优先级高于`Element.hidden`。如果 CSS 指定了该元素不可见(`display: none`)或可见(`visibility: visible`),那么`Element.hidden`并不能改变该元素实际的可见性。换言之,这个属性只在 CSS 没有明确设定当前元素的可见性时才有效。
**(2)Element.contentEditable,Element.isContentEditable**
@@ -371,8 +371,8 @@ document.body.scrollHeight
```javascript
// HTML 代码如下
-// ...
-document.getElementById('myDiv').scrollHeight // 356
+//
...
+document.getElementById('myDiv').scrollHeight // 200
```
上面代码中,即使`myDiv`元素的 CSS 高度只有200像素,且溢出部分不可见,但是`scrollHeight`仍然会返回该元素的原始高度。
diff --git a/docs/dom/mutationobserver.md b/docs/dom/mutationobserver.md
index 12651c2..0715565 100644
--- a/docs/dom/mutationobserver.md
+++ b/docs/dom/mutationobserver.md
@@ -44,7 +44,7 @@ var observer = new MutationObserver(function (mutations, observer) {
```javascript
var article = document.querySelector('article');
-var options = {
+var options = {
'childList': true,
'attributes':true
} ;
@@ -98,7 +98,7 @@ var observer = new MutationObserver(function(mutations) {
observer.observe(document, { childList: true, subtree: true });
```
-### disconnect(),takeRecords()
+### disconnect(),takeRecords()
`disconnect()`方法用来停止观察。调用该方法后,DOM 再发生变动,也不会触发观察器。
diff --git a/docs/dom/nodelist.md b/docs/dom/nodelist.md
index f5c9457..bda3939 100644
--- a/docs/dom/nodelist.md
+++ b/docs/dom/nodelist.md
@@ -179,7 +179,7 @@ var img0 = c.item(0);
### HTMLCollection.prototype.namedItem()
-`namedItem`方法的参数是一个字符串,表示`id`属性或`name`属性的值,返回对应的元素节点。如果没有对应的节点,则返回`null`。
+`namedItem`方法的参数是一个字符串,表示`id`属性或`name`属性的值,返回当前集合中对应的元素节点。如果没有对应的节点,则返回`null`。
```javascript
// HTML 代码如下
@@ -188,3 +188,6 @@ var img0 = c.item(0);
var pic = document.getElementById('pic');
document.images.namedItem('pic') === pic // true
```
+
+`Collection.namedItem('value')`等同于`Collection['value']`。
+
diff --git a/docs/dom/parentnode.md b/docs/dom/parentnode.md
index 1aa2d0c..a4183a1 100644
--- a/docs/dom/parentnode.md
+++ b/docs/dom/parentnode.md
@@ -54,9 +54,11 @@ document.body.childElementCount // 13
### ParentNode.append(),ParentNode.prepend()
-`append`方法为当前节点追加一个或多个子节点,位置是最后一个元素子节点的后面。
+**(1)ParentNode.append()**
-该方法不仅可以添加元素子节点,还可以添加文本子节点。
+`append()`方法为当前节点追加一个或多个子节点,位置是最后一个元素子节点的后面。
+
+该方法不仅可以添加元素子节点(参数为元素节点),还可以添加文本子节点(参数为字符串)。
```javascript
var parent = document.body;
@@ -78,9 +80,17 @@ var p = document.createElement('p');
parent.append('Hello', p);
```
-注意,该方法没有返回值。
+该方法没有返回值。
+
+注意,该方法与`Node.prototype.appendChild()`方法有三点不同。
+
+- `append()`允许字符串作为参数,`appendChild()`只允许子节点作为参数。
+- `append()`没有返回值,而`appendChild()`返回添加的子节点。
+- `append()`可以添加多个子节点和字符串(即允许多个参数),`appendChild()`只能添加一个节点(即只允许一个参数)。
+
+**(2)ParentNode.prepend()**
-`prepend`方法为当前节点追加一个或多个子节点,位置是第一个元素子节点的前面。它的用法与`append`方法完全一致,也是没有返回值。
+`prepend()`方法为当前节点追加一个或多个子节点,位置是第一个元素子节点的前面。它的用法与`append()`方法完全一致,也是没有返回值。
## ChildNode 接口
@@ -88,7 +98,7 @@ parent.append('Hello', p);
### ChildNode.remove()
-`remove`方法用于从父节点移除当前节点。
+`remove()`方法用于从父节点移除当前节点。
```javascript
el.remove()
@@ -98,7 +108,9 @@ el.remove()
### ChildNode.before(),ChildNode.after()
-`before`方法用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点。
+**(1)ChildNode.before()**
+
+`before()`方法用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点。
注意,该方法不仅可以插入元素节点,还可以插入文本节点。
@@ -119,11 +131,13 @@ el.before(p, p1);
el.before(p, 'Hello');
```
-`after`方法用于在当前节点的后面,插入一个或多个同级节点,两者拥有相同的父节点。用法与`before`方法完全相同。
+**(2)ChildNode.after()**
+
+`after()`方法用于在当前节点的后面,插入一个或多个同级节点,两者拥有相同的父节点。用法与`before`方法完全相同。
### ChildNode.replaceWith()
-`replaceWith`方法使用参数节点,替换当前节点。参数可以是元素节点,也可以是文本节点。
+`replaceWith()`方法使用参数节点,替换当前节点。参数可以是元素节点,也可以是文本节点。
```javascript
var span = document.createElement('span');
@@ -131,3 +145,4 @@ el.replaceWith(span);
```
上面代码中,`el`节点将被`span`节点替换。
+
diff --git a/docs/elements/input.md b/docs/elements/input.md
index d64c55a..fce544a 100644
--- a/docs/elements/input.md
+++ b/docs/elements/input.md
@@ -75,7 +75,18 @@
- `labels`:返回一个`NodeList`实例,代表绑定当前`
`节点的`