diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 7f7966340c..2614a09737 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -106,9 +106,9 @@ JavaScript 是将这三件事结合在一起的唯一的浏览器技术。 此类语言的示例有: -- [CoffeeScript](http://coffeescript.org/) 是 JavaScript 的一种语法糖。它引入了更加简短的语法,使我们可以编写更清晰简洁的代码。通常,Ruby 开发者喜欢它。 -- [TypeScript](http://www.typescriptlang.org/) 专注于添加“严格的数据类型”以简化开发,以更好地支持复杂系统的开发。由微软开发。 -- [Flow](http://flow.org/) 也添加了数据类型,但是以一种不同的方式。由 Facebook 开发。 +- [CoffeeScript](https://coffeescript.org/) 是 JavaScript 的一种语法糖。它引入了更加简短的语法,使我们可以编写更清晰简洁的代码。通常,Ruby 开发者喜欢它。 +- [TypeScript](https://www.typescriptlang.org/) 专注于添加“严格的数据类型”以简化开发,以更好地支持复杂系统的开发。由微软开发。 +- [Flow](https://flow.org/) 也添加了数据类型,但是以一种不同的方式。由 Facebook 开发。 - [Dart](https://www.dartlang.org/) 是一门独立的语言。它拥有自己的引擎,该引擎可以在非浏览器环境中运行(例如手机应用),它也可以被编译成 JavaScript。由 Google 开发。 - [Brython](https://brython.info/) 是一个 Python 到 JavaScript 的转译器,让我们可以在不使用 JavaScript 的情况下,以纯 Python 编写应用程序。 - [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) 是一个现代、简洁且安全的编程语言,编写出的应用程序可以在浏览器和 Node 环境中运行。 diff --git a/1-js/01-getting-started/2-manuals-specifications/article.md b/1-js/01-getting-started/2-manuals-specifications/article.md index 8d04a3fb46..84f4c2d7bd 100644 --- a/1-js/01-getting-started/2-manuals-specifications/article.md +++ b/1-js/01-getting-started/2-manuals-specifications/article.md @@ -29,7 +29,7 @@ JavaScript 是一门还在发展中的语言,定期会添加一些新的功能 要查看它们在基于浏览器的引擎及其他引擎中的支持情况,请看: -- —— 每个功能的支持表,例如,查看哪个引擎支持现代加密(cryptography)函数:。 +- —— 每个功能的支持表,例如,查看哪个引擎支持现代加密(cryptography)函数:。 - —— 一份列有语言功能以及引擎是否支持这些功能的表格。 所有这些资源在实际开发中都有用武之地,因为它们包含了有关语言细节,以及它们被支持的程度等非常有价值的信息。 diff --git a/1-js/01-getting-started/3-code-editors/article.md b/1-js/01-getting-started/3-code-editors/article.md index de5c1038e6..8d9c4fbe4e 100644 --- a/1-js/01-getting-started/3-code-editors/article.md +++ b/1-js/01-getting-started/3-code-editors/article.md @@ -13,7 +13,7 @@ IDE 加载项目(通常包含很多文件),并且允许在不同文件之 如果你还没考虑好选哪一款 IDE,可以考虑下面两个: - [Visual Studio Code](https://code.visualstudio.com/)(跨平台,免费)。 -- [WebStorm](http://www.jetbrains.com/webstorm/)(跨平台,收费)。 +- [WebStorm](https://www.jetbrains.com/webstorm/)(跨平台,收费)。 对于 Windows 系统来说,也有个叫 "Visual Studio" 的 IDE,请不要跟 "Visual Studio Code" 混淆。"Visual Studio" 是一个收费的、强大的 Windows 专用编辑器,它十分适合于 .NET 开发。用它进行 JavaScript 开发也不错。"Visual Studio" 有个免费的版本 [Visual Studio Community](https://www.visualstudio.com/vs/community/)。 @@ -31,7 +31,6 @@ IDE 加载项目(通常包含很多文件),并且允许在不同文件之 下面是一些值得你关注的“轻量编辑器”: -- [Atom](https://atom.io/)(跨平台,免费)。 - [Sublime Text](http://www.sublimetext.com)(跨平台,共享软件)。 - [Notepad++](https://notepad-plus-plus.org/)(Windows,免费)。 - [Vim](http://www.vim.org/) 和 [Emacs](https://www.gnu.org/software/emacs/) 也很棒,如果你知道怎么使用它们的话。 diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index 4823932dbb..d061b3c3f1 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -64,6 +64,7 @@ let message = 'Hello'; ``` 一些程序员采用下面的形式书写多个变量: + ```js no-beautify let user = 'John', age = 25, @@ -103,6 +104,7 @@ let user = 'John' 我们可以在盒子内放入任何值。 并且,这个盒子的值,我们想改变多少次,就可以改变多少次: + ```js run let message; @@ -192,7 +194,7 @@ let my-name; // 连字符 '-' 不允许用于变量命名 ``` ```smart header="区分大小写" -命名为 `apple` 和 `AppLE` 的变量是不同的两个变量。 +命名为 `apple` 和 `APPLE` 的变量是不同的两个变量。 ``` ````smart header="允许非英文字母,但不推荐" @@ -260,7 +262,6 @@ myBirthday = '01.01.2001'; // 错误,不能对常量重新赋值 当程序员能确定这个变量永远不会改变的时候,就可以使用 `const` 来确保这种行为,并且清楚地向别人传递这一事实。 - ### 大写形式的常数 一个普遍的做法是将常量用作别名,以便记住那些在执行之前就已知的难以记住的值。 @@ -291,6 +292,7 @@ alert(color); // #FF7F00 作为一个“常数”,意味着值永远不变。但是有些常量在执行之前就已知了(比如红色的十六进制值),还有些在执行期间被“计算”出来,但初始赋值之后就不会改变。 例如: + ```js const pageLoadTime = /* 网页加载所需的时间 */; ``` diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index e2f02e3af2..fe2a9905b4 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -68,9 +68,20 @@ n = 12.345; ## BigInt 类型 [#bigint-type] -在 JavaScript 中,"number" 类型无法表示大于 (253-1)(即 `9007199254740991`),或小于 -(253-1) 的整数。这是其内部表示形式导致的技术限制。 +在 JavaScript 中,"number" 类型无法安全地表示大于 (253-1)(即 `9007199254740991`),或小于 -(253-1) 的整数。 -在大多数情况下,这个范围就足够了,但有时我们需要很大的数字,例如用于加密或微秒精度的时间戳。 +更准确的说,"number" 类型可以存储更大的整数(最多 1.7976931348623157 * 10308),但超出安全整数范围 ±(253-1) 会出现精度问题,因为并非所有数字都适合固定的 64 位存储。因此,可能存储的是“近似值”。 + +例如,这两个数字(正好超出了安全整数范围)是相同的: + +```js +console.log(9007199254740991 + 1); // 9007199254740992 +console.log(9007199254740991 + 2); // 9007199254740992 +``` + +也就是说,所有大于 (253-1) 的奇数都不能用 "number" 类型存储。 + +在大多数情况下,±(253-1) 范围就足够了,但有时候我们需要整个范围非常大的整数,例如用于密码学或微秒精度的时间戳。 `BigInt` 类型是最近被添加到 JavaScript 语言中的,用于表示任意长度的整数。 @@ -261,16 +272,18 @@ typeof alert // "function" (3) ## 总结 -JavaScript 中有八种基本的数据类型(译注:前七种为基本数据类型,也称为原始类型,而 `object` 为复杂数据类型)。 - -- `number` 用于任何类型的数字:整数或浮点数,在 ±(253-1) 范围内的整数。 -- `bigint` 用于任意长度的整数。 -- `string` 用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。 -- `boolean` 用于 `true` 和 `false`。 -- `null` 用于未知的值 —— 只有一个 `null` 值的独立类型。 -- `undefined` 用于未定义的值 —— 只有一个 `undefined` 值的独立类型。 -- `symbol` 用于唯一的标识符。 -- `object` 用于更复杂的数据结构。 +JavaScript 中有八种基本的数据类型(译注:前七种为基本数据类型,也称为原始数据类型,而 `object` 为复杂数据类型)。 + +- 七种原始数据类型: + - `number` 用于任何类型的数字:整数或浮点数,在 ±(253-1) 范围内的整数。 + - `bigint` 用于任意长度的整数。 + - `string` 用于字符串:一个字符串可以包含 0 个或多个字符,所以没有单独的单字符类型。 + - `boolean` 用于 `true` 和 `false`。 + - `null` 用于未知的值 —— 只有一个 `null` 值的独立类型。 + - `undefined` 用于未定义的值 —— 只有一个 `undefined` 值的独立类型。 + - `symbol` 用于唯一的标识符。 +- 以及一种非原始数据类型: + - `object` 用于更复杂的数据结构。 我们可以通过 `typeof` 运算符查看存储在变量中的数据类型。 diff --git a/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md b/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md index 4ed9b855b9..fb6ad4a7e6 100644 --- a/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md +++ b/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md @@ -1,6 +1,6 @@ 答案:`null`,因为它是列表中第一个假值。 ```js run -alert( 1 && null && 2 ); +alert(1 && null && 2); ``` diff --git a/1-js/02-first-steps/13-while-for/article.md b/1-js/02-first-steps/13-while-for/article.md index d6b81aacbb..42744fe9a6 100644 --- a/1-js/02-first-steps/13-while-for/article.md +++ b/1-js/02-first-steps/13-while-for/article.md @@ -6,6 +6,19 @@ **循环** 是一种重复运行同一代码的方法。 +```smart header="for..of 和 for..in 循环" +给进阶读者的一个小提示。 + +本文仅涵盖了基础的循环:`while`,`do..while` 和 `for(..; ..; ..)`。 + +如果你阅读本文是为了寻找其他类型的循环,那么: + +- 用于遍历对象属性的 `for..in` 循环请见:[for..in](info:object#forin)。 +- 用于遍历数组和可迭代对象的循环分别请见:[for..of](info:array#loops) 和 [iterables](info:iterable)。 + +否则,请继续阅读。 +``` + ## "while" 循环 `while` 循环的语法如下: @@ -162,10 +175,8 @@ for (i = 0; i < 3; i++) { // 使用现有的变量 alert(i); //3,可见,因为是在循环之外声明的 ``` - ```` - ### 省略语句段 `for` 循环的任何语句段都可以被省略。 @@ -286,7 +297,6 @@ if (i > 5) { ……用问号重写: - ```js no-beautify (i > 5) ? alert(i) : *!*continue*/!*; // continue 不允许在这个位置 ``` @@ -321,6 +331,7 @@ alert('Done!'); 在 `input` 之后的普通 `break` 只会打破内部循环。这还不够 —— 标签可以实现这一功能! **标签** 是在循环之前带有冒号的标识符: + ```js labelName: for (...) { ... @@ -342,6 +353,7 @@ labelName: for (...) { // 用得到的值做些事…… } } + alert('Done!'); ``` @@ -362,6 +374,7 @@ for (let i = 0; i < 3; i++) { ... } 标签不允许我们跳到代码的任意位置。 例如,这样做是不可能的: + ```js break label; // 跳转至下面的 label 处(无效) @@ -369,6 +382,7 @@ label: for (...) ``` `break` 指令必须在代码块内。从技术上讲,任何被标记的代码块都有效,例如: + ```js label: { // ... diff --git a/1-js/02-first-steps/16-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md index a7e71b6c9d..b50af60624 100644 --- a/1-js/02-first-steps/16-function-expressions/article.md +++ b/1-js/02-first-steps/16-function-expressions/article.md @@ -150,7 +150,7 @@ ask("Do you agree?", showOk, showCancel); 主要思想是我们传递一个函数,并期望在稍后必要时将其“回调”。在我们的例子中,`showOk` 是回答 "yes" 的回调,`showCancel` 是回答 "no" 的回调。 -我们可以用函数表达式对同样的函数进行大幅简写: +我们可以使用函数表达式来编写一个等价的、更简洁的函数: ```js run no-beautify function ask(question, yes, no) { @@ -186,7 +186,7 @@ ask( 首先是语法:如何通过代码对它们进行区分。 -- **函数声明**:在主代码流中声明为单独的语句的函数。 +- **函数声明**:在主代码流中声明为单独的语句的函数: ```js // 函数声明 diff --git a/1-js/02-first-steps/18-javascript-specials/article.md b/1-js/02-first-steps/18-javascript-specials/article.md index ed567d0670..4631c44008 100644 --- a/1-js/02-first-steps/18-javascript-specials/article.md +++ b/1-js/02-first-steps/18-javascript-specials/article.md @@ -55,7 +55,7 @@ for(;;) { 该指令必须位于 JavaScript 脚本的顶部或函数体的开头。 -如果没有 `"use strict"`,所有东西仍可以正常工作,但是某些特性的表现方式与旧式「兼容」方式相同。我们通常更喜欢现代的方式。 +如果没有 `"use strict"`,所有东西仍可以正常工作,但某些功能将以老式的“兼容”方式运行。我们通常更喜欢现代的方式。 语言的一些现代特征(比如我们将来要学习的类)会隐式地启用严格模式。 @@ -144,7 +144,7 @@ JavaScript 支持以下运算符: : 简单的赋值:`a = b` 和合并了其他操作的赋值:`a * = 2`。 按位运算符 -: 按位运算符在最低位级上操作 32 位的整数:详见 [文档](mdn:/JavaScript/Guide/Expressions_and_Operators#Bitwise)。 +: 按位运算符在最低位级上操作 32 位的整数:详见 [文档](mdn:/JavaScript/Guide/Expressions_and_Operators#bitwise_operators)。 三元运算符 : 唯一具有三个参数的操作:`cond ? resultA : resultB`。如果 `cond` 为真,则返回 `resultA`,否则返回 `resultB`。 diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg index a3c7db6ec4..5fc6dce3aa 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg @@ -1 +1 @@ -open sources \ No newline at end of file +open sources \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg index 6e7b60f854..63bf4966e2 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg @@ -1 +1 @@ -here's the listbreakpoints \ No newline at end of file +here's the listbreakpoints \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg index d5d2a0b934..3fe5f124f2 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg index 83468fddb1..0147c2e0aa 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg @@ -1 +1 @@ -213see the outer call detailswatch expressionscurrent variables \ No newline at end of file +213see the outer call detailswatch expressionscurrent variables \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg index 23937e0d68..9fa1b3b8cc 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg @@ -1 +1 @@ -nested calls \ No newline at end of file +nested calls \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg index 41a3d8784b..0167082569 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg @@ -1 +1 @@ -213 \ No newline at end of file +213 \ No newline at end of file diff --git a/1-js/03-code-quality/03-comments/article.md b/1-js/03-code-quality/03-comments/article.md index 7760c451a6..b65260e8c2 100644 --- a/1-js/03-code-quality/03-comments/article.md +++ b/1-js/03-code-quality/03-comments/article.md @@ -143,7 +143,7 @@ function pow(x, n) { 顺便说一句,很多诸如 [WebStorm](https://www.jetbrains.com/webstorm/) 这样的编辑器,都可以很好地理解和使用这些注释,来提供自动补全和一些自动化代码检查工作。 -当然,也有一些像 [JSDoc 3](https://github.com/jsdoc3/jsdoc) 这样的工具,可以通过注释直接生成 HTML 文档。你可以在 阅读更多关于 JSDoc 的信息。 +当然,也有一些像 [JSDoc 3](https://github.com/jsdoc/jsdoc) 这样的工具,可以通过注释直接生成 HTML 文档。你可以在 阅读更多关于 JSDoc 的信息。 为什么任务以这种方式解决? : 写了什么代码很重要。但是为什么 **不** 那样写可能对于理解正在发生什么更重要。为什么任务是通过这种方式解决的?代码并没有给出答案。 diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 1a23a48e39..931a32f107 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -69,7 +69,7 @@ if (!Math.trunc) { // 如果没有这个函数 } ``` -JavaScript 是一种高度动态的语言,脚本可以添加/修改任何函数,甚至包括内建函数。 +JavaScript 是一种高度动态的语言。脚本可以添加/修改任何函数,甚至包括内建函数。 两个有趣的 polyfill 库: - [core js](https://github.com/zloirock/core-js) 支持了很多特性,允许只包含需要的特性。 diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 933d719823..aea576f926 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -355,7 +355,7 @@ alert( "test" in obj ); // true,属性存在! 这种情况很少发生,因为通常情况下不应该给对象赋值 `undefined`。我们通常会用 `null` 来表示未知的或者空的值。因此,`in` 运算符是代码中的特殊来宾。 -## "for..in" 循环 +## "for..in" 循环 [#forin] 为了遍历一个对象的所有键(key),可以使用一个特殊形式的循环:`for..in`。这跟我们在前面学到的 `for(;;)` 循环是完全不一样的东西。 diff --git a/1-js/04-object-basics/02-object-copy/article.md b/1-js/04-object-basics/02-object-copy/article.md index 169a7376b2..ee7cd5bce3 100644 --- a/1-js/04-object-basics/02-object-copy/article.md +++ b/1-js/04-object-basics/02-object-copy/article.md @@ -104,11 +104,9 @@ alert( a == b ); // false 那么,拷贝一个对象变量会又创建一个对相同对象的引用。 -但是,如果我们想要复制一个对象,那该怎么做呢?创建一个独立的拷贝,克隆? +但是,如果我们想要复制一个对象,那该怎么做呢? -这也是可行的,但稍微有点困难,因为 JavaScript 没有提供对此操作的内建的方法。但很少需要这样做 —— 通过引用进行拷贝在大多数情况下已经满足了。 - -但是,如果我们真的想要这样做,那么就需要创建一个新对象,并通过遍历现有属性的结构,在原始类型值的层面,将其复制到新对象,以复制已有对象的结构。 +我们可以创建一个新对象,通过遍历已有对象的属性,并在原始类型值的层面复制它们,以实现对已有对象结构的复制。 就像这样: @@ -190,7 +188,7 @@ let clone = Object.assign({}, user); ## 深层克隆 -到现在为止,我们都假设 `user` 的所有属性均为原始类型。但属性可以是对其他对象的引用。那应该怎样处理它们呢? +到现在为止,我们都假设 `user` 的所有属性均为原始类型。但属性可以是对其他对象的引用。 例如: ```js run @@ -207,8 +205,6 @@ alert( user.sizes.height ); // 182 现在这样拷贝 `clone.sizes = user.sizes` 已经不足够了,因为 `user.sizes` 是个对象,它会以引用形式被拷贝。因此 `clone` 和 `user` 会共用一个 sizes: -就像这样: - ```js run let user = { name: "John", @@ -224,10 +220,10 @@ alert( user.sizes === clone.sizes ); // true,同一个对象 // user 和 clone 分享同一个 sizes user.sizes.width++; // 通过其中一个改变属性值 -alert(clone.sizes.width); // 51,能从另外一个看到变更的结果 +alert(clone.sizes.width); // 51,能从另外一个获取到变更后的结果 ``` -为了解决这个问题,我们应该使用一个拷贝循环来检查 `user[key]` 的每个值,如果它是一个对象,那么也复制它的结构。这就是所谓的“深拷贝”。 +为了解决这个问题,并让 `user` 和 `clone` 成为两个真正独立的对象,我们应该使用一个拷贝循环来检查 `user[key]` 的每个值,如果它是一个对象,那么也复制它的结构。这就是所谓的“深拷贝”。 我们可以使用递归来实现它。或者为了不重复造轮子,采用现有的实现,例如 [lodash](https://lodash.com) 库的 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)。 diff --git a/1-js/04-object-basics/03-garbage-collection/article.md b/1-js/04-object-basics/03-garbage-collection/article.md index 88d3a0a381..9354c16d15 100644 --- a/1-js/04-object-basics/03-garbage-collection/article.md +++ b/1-js/04-object-basics/03-garbage-collection/article.md @@ -70,11 +70,12 @@ let admin = user; ![](memory-user-john-admin.svg) 现在如果执行刚刚的那个操作: + ```js user = null; ``` -……然后对象仍然可以被通过 `admin` 这个全局变量访问到,所以对象还在内存中。如果我们又重写了 `admin`,对象就会被删除。 +……然后对象仍然可以被通过 `admin` 这个全局变量访问到,因此它必须被保留在内存中。如果我们又重写了 `admin`,对象就会被删除。 ## 相互关联的对象 @@ -169,7 +170,7 @@ family = null; ![](garbage-collection-2.svg) -然后它们的引用被标记了: +然后,我们跟随它们的引用标记它们所引用的对象: ![](garbage-collection-3.svg) @@ -183,12 +184,12 @@ family = null; 我们还可以将这个过程想象成从根溢出一个巨大的油漆桶,它流经所有引用并标记所有可到达的对象。然后移除未标记的。 -这是垃圾收集工作的概念。JavaScript 引擎做了许多优化,使垃圾回收运行速度更快,并且不影响正常代码运行。 +这是垃圾收集工作的概念。JavaScript 引擎做了许多优化,使垃圾回收运行速度更快,并且不会对代码执行引入任何延迟。 一些优化建议: -- **分代收集(Generational collection)**—— 对象被分成两组:“新的”和“旧的”。许多对象出现,完成它们的工作并很快死去,它们可以很快被清理。那些长期存活的对象会变得“老旧”,而且被检查的频次也会减少。 -- **增量收集(Incremental collection)**—— 如果有许多对象,并且我们试图一次遍历并标记整个对象集,则可能需要一些时间,并在执行过程中带来明显的延迟。所以引擎试图将垃圾收集工作分成几部分来做。然后将这几部分会逐一进行处理。这需要它们之间有额外的标记来追踪变化,但是这样会有许多微小的延迟而不是一个大的延迟。 +- **分代收集(Generational collection)**—— 对象被分成两组:“新的”和“旧的”。在典型的代码中,许多对象的生命周期都很短:它们出现、完成它们的工作并很快死去,因此在这种情况下跟踪新对象并将其从内存中清除是有意义的。那些长期存活的对象会变得“老旧”,并且被检查的频次也会降低。 +- **增量收集(Incremental collection)**—— 如果有许多对象,并且我们试图一次遍历并标记整个对象集,则可能需要一些时间,并在执行过程中带来明显的延迟。因此,引擎将现有的整个对象集拆分为多个部分,然后将这些部分逐一清除。这样就会有很多小型的垃圾收集,而不是一个大型的。这需要它们之间有额外的标记来追踪变化,但是这样会带来许多微小的延迟而不是一个大的延迟。 - **闲时收集(Idle-time collection)**—— 垃圾收集器只会在 CPU 空闲时尝试运行,以减少可能对代码执行的影响。 还有其他垃圾回收算法的优化和风格。尽管我想在这里描述它们,但我必须打住了,因为不同的引擎会有不同的调整和技巧。而且,更重要的是,随着引擎的发展,情况会发生变化,所以在没有真实需求的时候,“提前”学习这些内容是不值得的。当然,除非你纯粹是出于兴趣。我在下面给你提供了一些相关链接。 @@ -199,7 +200,7 @@ family = null; - 垃圾回收是自动完成的,我们不能强制执行或是阻止执行。 - 当对象是可达状态时,它一定是存在于内存中的。 -- 被引用与可访问(从一个根)不同:一组相互连接的对象可能整体都不可达。 +- 被引用与可访问(从一个根)不同:一组相互连接的对象可能整体都不可达,正如我们在上面的例子中看到的那样。 现代引擎实现了垃圾回收的高级算法。 @@ -207,6 +208,6 @@ family = null; 如果你熟悉底层(low-level)编程,关于 V8 引擎垃圾回收器的更详细信息请参阅文章 [V8 之旅:垃圾回收](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection)。 -[V8 博客](http://v8project.blogspot.com/) 还不时发布关于内存管理变化的文章。当然,为了学习垃圾收集,你最好通过学习 V8 引擎内部知识来进行准备,并阅读一个名为 [Vyacheslav Egorov](http://mrale.ph) 的 V8 引擎工程师的博客。我之所以说 “V8”,因为网上关于它的文章最丰富的。对于其他引擎,许多方法是相似的,但在垃圾收集上许多方面有所不同。 +[V8 博客](http://v8project.blogspot.com/) 还不时发布关于内存管理变化的文章。当然,为了学习更多垃圾收集的相关内容,你最好通过学习 V8 引擎内部知识来进行准备,并阅读一个名为 [Vyacheslav Egorov](http://mrale.ph) 的 V8 引擎工程师的博客。我之所以说 “V8”,因为网上关于它的文章最丰富的。对于其他引擎,许多方法是相似的,但在垃圾收集上许多方面有所不同。 当你需要底层的优化时,对引擎有深入了解将很有帮助。在熟悉了这门编程语言之后,把熟悉引擎作为下一步计划是明智之选。 diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 8f87eb62e3..9ed01e8edc 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -90,7 +90,7 @@ let user = { 如上所示,我们可以省略 `"function"`,只写 `sayHi()`。 -说实话,这种表示法还是有些不同。在对象继承方面有一些细微的差别(稍后将会介绍),但目前它们并不重要。在几乎所有的情况下,较短的语法是首选的。 +说实话,这种表示法还是有些不同。在对象继承方面有一些细微的差别(稍后将会介绍),但目前它们并不重要。在几乎所有的情况下,更短的语法是首选的。 ## 方法中的 "this" diff --git a/1-js/04-object-basics/08-symbol/article.md b/1-js/04-object-basics/08-symbol/article.md index a8047f208d..3223721473 100644 --- a/1-js/04-object-basics/08-symbol/article.md +++ b/1-js/04-object-basics/08-symbol/article.md @@ -103,11 +103,11 @@ alert( user[id] ); // 我们可以使用 symbol 作为键来访问数据 使用 `Symbol("id")` 作为键,比起用字符串 `"id"` 来有什么好处呢? -因为 `user` 对象属于其他的代码,那些代码也会使用这个对象,所以我们不应该在它上面直接添加任何字段,这样很不安全。但是你添加的 symbol 属性不会被意外访问到,第三方代码根本不会看到它,所以使用 symbol 基本上不会有问题。 +由于 `user` 对象属于另一个代码库,所以向它们添加字段是不安全的,因为我们可能会影响代码库中的其他预定义行为。但 symbol 属性不会被意外访问到。第三方代码不会知道新定义的 symbol,因此将 symbol 添加到 `user` 对象是安全的。 -另外,假设另一个脚本希望在 `user` 中有自己的标识符,以实现自己的目的。这可能是另一个 JavaScript 库,因此脚本之间完全不了解彼此。 +另外,假设另一个脚本希望在 `user` 中有自己的标识符,以实现自己的目的。 -然后该脚本可以创建自己的 `Symbol("id")`,像这样: +那么,该脚本可以创建自己的 `Symbol("id")`,像这样: ```js // ... @@ -169,7 +169,7 @@ for (let key in user) alert(key); // name, age(没有 symbol) */!* // 使用 symbol 任务直接访问 -alert( "Direct: " + user[id] ); +alert("Direct: " + user[id]); // Direct: 123 ``` [Object.keys(user)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。 @@ -222,7 +222,7 @@ alert( id === idAgain ); // true ### Symbol.keyFor -对于全局 symbol,不仅有 `Symbol.for(key)` 按名字返回一个 symbol,还有一个反向调用:`Symbol.keyFor(sym)`,它的作用完全反过来:通过全局 symbol 返回一个名字。 +我们已经看到,对于全局 symbol,`Symbol.for(key)` 按名字返回一个 symbol。相反,通过全局 symbol 返回一个名字,我们可以使用 `Symbol.keyFor(sym)`: 例如: @@ -238,7 +238,7 @@ alert( Symbol.keyFor(sym2) ); // id `Symbol.keyFor` 内部使用全局 symbol 注册表来查找 symbol 的键。所以它不适用于非全局 symbol。如果 symbol 不是全局的,它将无法找到它并返回 `undefined`。 -也就是说,任何 symbol 都具有 `description` 属性。 +也就是说,所有 symbol 都具有 `description` 属性。 例如: @@ -286,4 +286,4 @@ symbol 有两个主要的使用场景: 2. JavaScript 使用了许多系统 symbol,这些 symbol 可以作为 `Symbol.*` 访问。我们可以使用它们来改变一些内建行为。例如,在本教程的后面部分,我们将使用 `Symbol.iterator` 来进行 [迭代](info:iterable) 操作,使用 `Symbol.toPrimitive` 来设置 [对象原始值的转换](info:object-toprimitive) 等等。 -从技术上说,symbol 不是 100% 隐藏的。有一个内建方法 [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) 允许我们获取所有的 symbol。还有一个名为 [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) 的方法可以返回一个对象的 **所有** 键,包括 symbol。所以它们并不是真正的隐藏。但是大多数库、内建方法和语法结构都没有使用这些方法。 +从技术上说,symbol 不是 100% 隐藏的。有一个内建方法 [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) 允许我们获取所有的 symbol。还有一个名为 [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) 的方法可以返回一个对象的 **所有** 键,包括 symbol。但大多数库、内建方法和语法结构都没有使用这些方法。 diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index 6c486e74b4..0c26ed53fd 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -94,7 +94,7 @@ alert( typeof 0 ); // "number" alert( typeof new Number(0) ); // "object"! ``` -对象在 `if` 中始终为真,因此此处的 alert 将显示: +对象在 `if` 中始终为真,所以此处的 alert 将显示: ```js run let zero = new Number(0); @@ -104,9 +104,10 @@ if (zero) { // zero 为 true,因为它是一个对象 } ``` -另一方面,调用不带 `new`(关键字)的 `String/Number/Boolean` 函数是完全理智和有用的。它们将一个值转换为相应的类型:转成字符串、数字或布尔值(原始类型)。 +另一方面,调用不带 `new`(关键字)的 `String/Number/Boolean` 函数是可以的且有效的。它们将一个值转换为相应的类型:转成字符串、数字或布尔值(原始类型)。 例如,下面完全是有效的: + ```js let num = Number("123"); // 将字符串转成数字 ``` diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index e055efe1bd..38684bec20 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -2,9 +2,9 @@ 在现代 JavaScript 中,数字(number)有两种类型: -1. JavaScript 中的常规数字以 64 位的格式 [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision) 存储,也被称为“双精度浮点数”。这是我们大多数时候所使用的数字,我们将在本章中学习它们。 +1. JavaScript 中的常规数字以 64 位的格式 [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) 存储,也被称为“双精度浮点数”。这是我们大多数时候所使用的数字,我们将在本章中学习它们。 -2. BigInt 数字,用于表示任意长度的整数。有时会需要它们,因为常规数字不能安全地超过 253 或小于 -253。由于仅在少数特殊领域才会用到 BigInt,因此我们在特殊的章节 中对其进行了介绍。 +2. BigInt 用于表示任意长度的整数。有时会需要它们,因为正如我们在前面的章节 中提到的,常规整数不能安全地超过 (253-1) 或小于 -(253-1)。由于仅在少数特殊领域才会用到 BigInt,因此我们在特殊的章节 中对其进行了介绍。 所以,在这里我们将讨论常规数字类型。现在让我们开始学习吧。 @@ -63,6 +63,9 @@ let mcs = 1e-6; // 1 的左边有 6 个 0 // -6 除以 1 后面跟着 6 个 0 的数字 1.23e-6 === 1.23 / 1000000; // 0.00000123 + +// 一个更大一点的数字的示例 +1234e-2 === 1234 / 100; // 12.34,小数点移动两次 ``` ### 十六进制,二进制和八进制数字 @@ -189,7 +192,7 @@ alert( num.toString(2) ); // 11111111 ## 不精确的计算 -在内部,数字是以 64 位格式 [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985) 表示的,所以正好有 64 位可以存储一个数字:其中 52 位被用于存储这些数字,其中 11 位用于存储小数点的位置(对于整数,它们为零),而 1 位用于符号。 +在内部,数字是以 64 位格式 [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754) 表示的,所以正好有 64 位可以存储一个数字:其中 52 位被用于存储这些数字,其中 11 位用于存储小数点的位置,而 1 位用于符号。 如果一个数字真的很大,则可能会溢出 64 位存储,变成一个特殊的数值 `Infinity`: @@ -253,7 +256,7 @@ alert( sum.toFixed(2) ); // 0.30 ```js run let sum = 0.1 + 0.2; -alert( +sum.toFixed(2) ); // 0.3 +alert( +sum.toFixed(2) ); // "0.30" ``` 我们可以将数字临时乘以 100(或更大的数字),将其转换为整数,进行数学运算,然后再除回。当我们使用整数进行数学运算时,误差会有所减少,但仍然可以在除法中得到: @@ -305,7 +308,7 @@ JavaScript 不会在此类事件中触发 error。它会尽最大努力使数字 alert( isNaN("str") ); // true ``` - 但是我们需要这个函数吗?我们不能只使用 `=== NaN` 比较吗?不好意思,这不行。值 "NaN" 是独一无二的,它不等于任何东西,包括它自身: + 但是我们需要这个函数吗?我们不能只使用 `=== NaN` 比较吗?很不幸,这不行。值 "NaN" 是独一无二的,它不等于任何东西,包括它自身: ```js run alert( NaN === NaN ); // false @@ -399,8 +402,8 @@ JavaScript 有一个内建的 [Math](https://developer.mozilla.org/en/docs/Web/J alert( Math.random() ); // ... (任何随机数) ``` -`Math.max(a, b, c...)` / `Math.min(a, b, c...)` -: 从任意数量的参数中返回最大/最小值。 +`Math.max(a, b, c...)` 和 `Math.min(a, b, c...)` +: 从任意数量的参数中返回最大值和最小值。 ```js run alert( Math.max(3, 5, -10, 0, 1) ); // 5 @@ -429,6 +432,11 @@ JavaScript 有一个内建的 [Math](https://developer.mozilla.org/en/docs/Web/J - `parseInt(str,base)` 将字符串 `str` 解析为在给定的 `base` 数字系统中的整数,`2 ≤ base ≤ 36`。 - `num.toString(base)` 将数字转换为在给定的 `base` 数字系统中的字符串。 +对于常规数字检测: + +- `isNaN(value)` 将其参数转换为数字,然后检测它是否为 `NaN` +- `isFinite(value)` 将其参数转换为数字,如果它是常规数字,则返回 `true`,而不是 `NaN/Infinity/-Infinity` + 要将 `12pt` 和 `100px` 之类的值转换为数字: - 使用 `parseInt/parseFloat` 进行“软”转换,它从字符串中读取数字,然后返回在发生 error 前可以读取到的值。 diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index 81181d1b5e..a65d33ffca 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -48,7 +48,7 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL * John"; ``` -当不考虑多行字符串的需要时,单引号和双引号来自语言创建的古时代。反引号出现较晚,因此更通用。 +单引号和双引号来自语言创建的的古老时代,当时没有考虑到多行字符串的需要。反引号出现较晚,因此更通用。 反引号还允许我们在第一个反引号之前指定一个“模版函数”。语法是:func`string`。函数 `func` 被自动调用,接收字符串和嵌入式表达式,并处理它们。你可以在 [docs](mdn:/JavaScript/Reference/Template_literals#Tagged_template_literals) 中阅读更多关于它们的信息。这叫做 "tagged templates"。此功能可以更轻松地将字符串包装到自定义模版或其他函数中,但这很少使用。 @@ -214,7 +214,7 @@ alert( 'Interface'.toLowerCase() ); // interface 或者我们想要使一个字符变成小写: -```js +```js run alert( 'Interface'[0].toLowerCase() ); // 'i' ``` @@ -371,8 +371,8 @@ alert( "Widget".includes("id", 3) ); // false, 从位置 3 开始没有 "id" 方法 [str.startsWith](mdn:js/String/startsWith) 和 [str.endsWith](mdn:js/String/endsWith) 的功能与其名称所表示的意思相同: ```js run -alert( "Widget".startsWith("Wid") ); // true,"Widget" 以 "Wid" 开始 -alert( "Widget".endsWith("get") ); // true,"Widget" 以 "get" 结束 +alert( "*!*Wid*/!*get".startsWith("Wid") ); // true,"Widget" 以 "Wid" 开始 +alert( "Wid*!*get*/!*".endsWith("get") ); // true,"Widget" 以 "get" 结束 ``` ## 获取子字符串 diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 486a75487f..8ebc6cee1f 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -281,7 +281,7 @@ fruits.age = 25; // 创建一个具有任意名称的属性 fruits.shift(); // 从首端取出一个元素 ``` -只获取并移除数字 `0` 对应的元素是不够的。其它元素也需要被重新编号。 +只获取并移除索引 `0` 对应的元素是不够的。其它元素也需要被重新编号。 `shift` 操作必须做三件事: @@ -512,21 +512,26 @@ alert('0' == '' ); // false,没有进一步的类型转换,是不同的字 数组是一种特殊的对象,适用于存储和管理有序的数据项。 -- 声明: +声明: - ```js - // 方括号 (常见用法) - let arr = [item1, item2...]; +```js +// 方括号 (常见用法) +let arr = [item1, item2...]; - // new Array (极其少见) - let arr = new Array(item1, item2...); - ``` +// new Array (极其少见) +let arr = new Array(item1, item2...); +``` - 调用 `new Array(number)` 会创建一个给定长度的数组,但不含有任何项。 +调用 `new Array(number)` 会创建一个给定长度的数组,但不含有任何项。 - `length` 属性是数组的长度,准确地说,它是数组最后一个数字索引值加一。它由数组方法自动调整。 - 如果我们手动缩短 `length`,那么数组就会被截断。 +获取元素: + +- 你可以通过元素的索引获取元素,例如 `arr[0]` +- 我们也可以使用允许负索引的 `at(i)` 方法。对于负值的 `i`,它会从数组的末尾往回数。如果 `i >= 0`,它的工作方式与 `arr[i]` 相同。 + 我们可以通过下列操作以双端队列的方式使用数组: - `push(...items)` 在末端添加 `items` 项。 diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 669302f2da..95461f7c68 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -234,12 +234,13 @@ arr.forEach(function(item, index, array) { ### indexOf/lastIndexOf 和 includes -[arr.indexOf](mdn:js/Array/indexOf)、[arr.lastIndexOf](mdn:js/Array/lastIndexOf) 和 [arr.includes](mdn:js/Array/includes) 方法与字符串操作具有相同的语法,并且作用基本上也与字符串的方法相同,只不过这里是对数组元素而不是字符进行操作: +[arr.indexOf](mdn:js/Array/indexOf) 和 [arr.includes](mdn:js/Array/includes) 方法语法相似,并且作用基本上也与字符串的方法相同,只不过这里是对数组元素而不是字符进行操作: - `arr.indexOf(item, from)` 从索引 `from` 开始搜索 `item`,如果找到则返回索引,否则返回 `-1`。 -- `arr.lastIndexOf(item, from)` —— 和上面相同,只是从右向左搜索。 - `arr.includes(item, from)` —— 从索引 `from` 开始搜索 `item`,如果找到则返回 `true`(译注:如果没找到,则返回 `false`)。 +通常使用这些方法时只会传入一个参数:传入 `item` 开始搜索。默认情况下,搜索是从头开始的。 + 例如: ```js run @@ -252,19 +253,31 @@ alert( arr.indexOf(null) ); // -1 alert( arr.includes(1) ); // true ``` -请注意,这些方法使用的是严格相等 `===` 比较。所以如果我们搜索 `false`,会精确到的确是 `false` 而不是数字 `0`。 +请注意,`indexOf` 使用严格相等 `===` 进行比较。所以,如果我们搜索 `false`,它会准确找到 `false` 而不是数字 `0`。 + +如果我们想检查数组中是否包含元素 `item`,并且不需要知道其确切的索引,那么 `arr.includes` 是首选。 + +方法 [arr.lastIndexOf](mdn:js/Array/lastIndexOf) 与 `indexOf` 相同,但从右向左查找。 -如果我们想检查是否包含某个元素,并且不想知道确切的索引,那么 `arr.includes` 是首选。 +```js run +let fruits = ['Apple', 'Orange', 'Apple']; + +alert( arr.indexOf('Apple') ); // 0(第一个 Apple) +alert( arr.lastIndexOf('Apple') ); // 2(最后一个 Apple) +``` -此外,`includes` 的一个非常小的差别是它能正确处理`NaN`,而不像 `indexOf/lastIndexOf`: +````smart header="方法 `includes` 可以正确的处理 `NaN`" +方法 `includes` 的一个次要但值得注意的特性是,它可以正确处理 `NaN`,这与 `indexOf` 不同: ```js run const arr = [NaN]; -alert( arr.indexOf(NaN) ); // -1(应该为 0,但是严格相等 === equality 对 NaN 无效) -alert( arr.includes(NaN) );// true(这个结果是对的) +alert( arr.indexOf(NaN) ); // -1(错,应该为 0) +alert( arr.includes(NaN) );// true(正确) ``` +这是因为 `includes` 是在比较晚的时候才被添加到 JavaScript 中的,并且在内部使用了更新了的比较算法。 +```` -### find 和 findIndex +### find 和 findIndex/findLastIndex 想象一下,我们有一个对象数组。我们如何找到具有特定条件的对象? @@ -304,7 +317,28 @@ alert(user.name); // John 注意在这个例子中,我们传给了 `find` 一个单参数函数 `item => item.id == 1`。这很典型,并且 `find` 方法的其他参数很少使用。 -[arr.findIndex](mdn:js/Array/findIndex) 方法(与 `arr.find` 方法)基本上是一样的,但它返回找到元素的索引,而不是元素本身。并且在未找到任何内容时返回 `-1`。 +[arr.findIndex](mdn:js/Array/findIndex) 方法(与 `arr.find`)具有相同的语法,但它返回找到的元素的索引,而不是元素本身。如果没找到,则返回 `-1`。 + +[arr.findLastIndex](mdn:js/Array/findLastIndex) 方法类似于 `findIndex`,但从右向左搜索,类似于 `lastIndexOf`。 + +这是一个例子: + +```js run +let users = [ + {id: 1, name: "John"}, + {id: 2, name: "Pete"}, + {id: 3, name: "Mary"}, + {id: 4, name: "John"} +]; + +// 寻找第一个 John 的索引 +alert(users.findIndex(user => user.name == 'John')); // 0 + +// 寻找最后一个 John 的索引 +alert(users.findLastIndex(user => user.name == 'John')); // 3 +``` + + ### filter @@ -389,6 +423,7 @@ alert( arr ); // *!*1, 15, 2*/!* 要使用我们自己的排序顺序,我们需要提供一个函数作为 `arr.sort()` 的参数。 该函数应该比较两个任意值并返回: + ```js function compare(a, b) { if (a > b) return 1; // 如果第一个值比第二个值大 @@ -633,7 +668,6 @@ arr.reduce((sum, current) => sum + current); [arr.reduceRight](mdn:js/Array/reduceRight) 和 [arr.reduce](mdn:js/Array/reduce) 方法的功能一样,只是遍历为从右到左。 - ## Array.isArray 数组是基于对象的,不构成单独的语言类型。 @@ -642,7 +676,7 @@ arr.reduce((sum, current) => sum + current); ```js run alert(typeof {}); // object -alert(typeof []); // same +alert(typeof []); // object(相同) ``` ……但是数组经常被使用,因此有一种特殊的方法用于判断:[Array.isArray(value)](mdn:js/Array/isArray)。如果 `value` 是一个数组,则返回 `true`;否则返回 `false`。 @@ -733,7 +767,7 @@ alert(soldiers[1].age); // 23 - `reduce/reduceRight(func, initial)` —— 通过对每个元素调用 `func` 计算数组上的单个值,并在调用之间传递中间结果。 - 其他: - - `Array.isArray(arr)` 检查 `arr` 是否是一个数组。 + - `Array.isArray(value)` 检查 `value` 是否是一个数组,如果是则返回 `true`,否则返回 `false`。 请注意,`sort`,`reverse` 和 `splice` 方法修改的是数组本身。 @@ -746,6 +780,7 @@ alert(soldiers[1].age); // 23 这两个方法的行为类似于 `||` 和 `&&` 运算符:如果 `fn` 返回一个真值,`arr.some()` 立即返回 `true` 并停止迭代其余数组项;如果 `fn` 返回一个假值,`arr.every()` 立即返回 `false` 并停止对其余数组项的迭代。 我们可以使用 `every` 来比较数组: + ```js run function arraysEqual(arr1, arr2) { return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]); diff --git a/1-js/05-data-types/11-date/1-new-date/solution.md b/1-js/05-data-types/11-date/1-new-date/solution.md index acceabd594..1b1622719c 100644 --- a/1-js/05-data-types/11-date/1-new-date/solution.md +++ b/1-js/05-data-types/11-date/1-new-date/solution.md @@ -13,6 +13,6 @@ alert( d1 ); ```js run // new Date(datestring) -let d2 = new Date("February 20, 2012 03:12:00"); +let d2 = new Date("2012-02-20T03:12"); alert( d2 ); ``` diff --git a/1-js/05-data-types/11-date/article.md b/1-js/05-data-types/11-date/article.md index 51592d87df..a44da1a2f7 100644 --- a/1-js/05-data-types/11-date/article.md +++ b/1-js/05-data-types/11-date/article.md @@ -57,7 +57,7 @@ `new Date(year, month, date, hours, minutes, seconds, ms)` : 使用当前时区中的给定组件创建日期。只有前两个参数是必须的。 - - `year` 必须是四位数:`2013` 是合法的,`98` 是不合法的。 + - `year` 应该是四位数。为了兼容性,也接受 2 位数,并将其视为 `19xx`,例如 `98` 与 `1998` 相同,但强烈建议始终使用 4 位数。 - `month` 计数从 `0`(一月)开始,到 `11`(十二月)结束。 - `date` 是当月的具体某一天,如果缺失,则为默认值 `1`。 - 如果 `hours/minutes/seconds/ms` 缺失,则均为默认值 `0`。 @@ -407,7 +407,7 @@ alert(ms); // 1327611110417 (时间戳) ```js run let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') ); -alert(date); +alert(date); ``` ## 总结 diff --git a/1-js/05-data-types/12-json/article.md b/1-js/05-data-types/12-json/article.md index 5809deba19..f27d217219 100644 --- a/1-js/05-data-types/12-json/article.md +++ b/1-js/05-data-types/12-json/article.md @@ -27,7 +27,7 @@ alert(user); // {name: "John", age: 30} ## JSON.stringify -[JSON](http://en.wikipedia.org/wiki/JSON)(JavaScript Object Notation)是表示值和对象的通用格式。在 [RFC 4627](https://tools.ietf.org/html/rfc4627) 标准中有对其的描述。最初它是为 JavaScript 而创建的,但许多其他编程语言也有用于处理它的库。因此,当客户端使用 JavaScript 而服务器端是使用 Ruby/PHP/Java 等语言编写的时,使用 JSON 可以很容易地进行数据交换。 +[JSON](https://en.wikipedia.org/wiki/JSON)(JavaScript Object Notation)是表示值和对象的通用格式。在 [RFC 4627](https://tools.ietf.org/html/rfc4627) 标准中有对其的描述。最初它是为 JavaScript 而创建的,但许多其他编程语言也有用于处理它的库。因此,当客户端使用 JavaScript 而服务器端是使用 Ruby/PHP/Java 等语言编写的时,使用 JSON 可以很容易地进行数据交换。 JavaScript 提供了如下方法: @@ -41,7 +41,7 @@ let student = { age: 30, isAdmin: false, courses: ['html', 'css', 'js'], - wife: null + spouse: null }; *!* @@ -58,7 +58,7 @@ alert(json); "age": 30, "isAdmin": false, "courses": ["html", "css", "js"], - "wife": null + "spouse": null } */ */!* diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md index cfb44bc5f1..85217e0715 100644 --- a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md @@ -1,3 +1,6 @@ +importance: 5 + +--- # if 内的函数 diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index e216a6d65e..7510d2a4f3 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -7,7 +7,7 @@ JavaScript 是一种非常面向函数的语言。它给了我们很大的自由 但是,如果在函数被创建之后,外部变量发生了变化会怎样?函数会获得新值还是旧值? -如果将函数作为参数传递并在代码中的另一个位置调用它,该函数将访问的是新位置的外部变量吗? +如果将函数作为参数(argument)传递并在代码中的另一个位置调用它,该函数将访问的是新位置的外部变量吗? 让我们通过本文来学习这些相关知识,以了解在这些场景以及更复杂的场景下到底会发生什么。 diff --git a/1-js/06-advanced-functions/06-function-object/article.md b/1-js/06-advanced-functions/06-function-object/article.md index d56f3a8794..0d379ca274 100644 --- a/1-js/06-advanced-functions/06-function-object/article.md +++ b/1-js/06-advanced-functions/06-function-object/article.md @@ -326,7 +326,7 @@ welcome(); // Hello, Guest(嵌套调用有效) 现在它可以正常运行了,因为名字 `func` 是函数局部域的。它不是从外部获取的(而且它对外部也是不可见的)。规范确保它只会引用当前函数。 -外部代码仍然有该函数的 `sayHi` 或 `welcome` 变量。而且 `func` 是一个“内部函数名”,可用于函数在自身内部进行自调用。 +外部代码仍然有该函数的 `sayHi` 或 `welcome` 变量。而且 `func` 是一个“内部函数名”,是函数可以可靠地调用自身的方式。 ```smart header="函数声明没有这个东西" 这里所讲的“内部名”特性只针对函数表达式,而不是函数声明。对于函数声明,没有用来添加“内部”名的语法。 diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md index 2e51ee3307..10efcc26e8 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md @@ -297,7 +297,7 @@ setTimeout(function run() { 例如,浏览器内的计时器可能由于许多原因而变慢: - CPU 过载。 - 浏览器页签处于后台模式。 -- 笔记本电脑用的是电池供电(译注:使用电池供电会以降低性能为代价提升续航)。 +- 笔记本电脑用的是省电模式。 所有这些因素,可能会将定时器的最小计时器分辨率(最小延迟)增加到 300ms 甚至 1000ms,具体以浏览器及其设置为准。 diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md index 23f0f2ee68..3571bcae3e 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md @@ -8,7 +8,7 @@ importance: 5 当被多次调用时,它会在每 `ms` 毫秒最多将调用传递给 `f` 一次。 -与去抖的不同是,它是个完全不同的装饰器: +与防抖(debounce)装饰器相比,其行为完全不同: - `debounce` 会在“冷却(cooldown)”期后运行函数一次。适用于处理最终结果。 - `throttle` 运行函数的频率不会大于所给定的时间 `ms` 毫秒。适用于不应该经常进行的定期更新。 diff --git a/1-js/08-prototypes/01-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md index 759bce8e88..d0bd65eba3 100644 --- a/1-js/08-prototypes/01-prototype-inheritance/article.md +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -131,7 +131,6 @@ alert(longEar.jumps); // true(从 rabbit) 当然,这可能很显而易见,但是仍然要强调:只能有一个 `[[Prototype]]`。一个对象不能从其他两个对象获得继承。 - ```smart header="`__proto__` 是 `[[Prototype]]` 的因历史原因而留下来的 getter/setter" 初学者常犯一个普遍的错误,就是不知道 `__proto__` 和 `[[Prototype]]` 的区别。 diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg deleted file mode 100644 index ede4e1227e..0000000000 --- a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg +++ /dev/null @@ -1 +0,0 @@ -eats: truename: "White Rabbit"animalRabbitrabbit[[Prototype]]prototype \ No newline at end of file diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index b372d27040..70af965682 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -3,15 +3,18 @@ 在这部分内容的第一章中,我们提到了设置原型的现代方法。 -`__proto__` 被认为是过时且不推荐使用的(deprecated),这里的不推荐使用是指 JavaScript 规范中规定,__proto__ 必须仅在浏览器环境下才能得到支持。 +使用 `obj.__proto__` 设置或读取原型被认为已经过时且不推荐使用(deprecated)了(已经被移至 JavaScript 规范的附录 B,意味着仅适用于浏览器)。 -现代的方法有: +现代的获取/设置原形的方法有: -- [Object.create(proto, [descriptors])](mdn:js/Object/create) —— 利用给定的 `proto` 作为 `[[Prototype]]` 和可选的属性描述来创建一个空对象。 - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) —— 返回对象 `obj` 的 `[[Prototype]]`。 - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) —— 将对象 `obj` 的 `[[Prototype]]` 设置为 `proto`。 -应该使用这些方法来代替 `__proto__`。 +`__proto__` 的不被反对的唯一的用法是在创建新对象时,将其用作属性:`{ __proto__: ... }`。 + +虽然,也有一种特殊的方法: + +- [Object.create(proto, [descriptors])](mdn:js/Object/create) —— 利用给定的 `proto` 作为 `[[Prototype]]` 和可选的属性描述来创建一个空对象。 例如: @@ -22,7 +25,7 @@ let animal = { // 创建一个以 animal 为原型的新对象 *!* -let rabbit = Object.create(animal); +let rabbit = Object.create(animal); // 与 {__proto__: animal} 相同 */!* alert(rabbit.eats); // true @@ -36,7 +39,9 @@ Object.setPrototypeOf(rabbit, {}); // 将 rabbit 的原型修改为 {} */!* ``` -`Object.create` 有一个可选的第二参数:属性描述器。我们可以在此处为新对象提供额外的属性,就像这样: +`Object.create` 方法更强大,因为它有一个可选的第二参数:属性描述器。 + +我们可以在此处为新对象提供额外的属性,就像这样: ```js run let animal = { @@ -57,26 +62,34 @@ alert(rabbit.jumps); // true 我们可以使用 `Object.create` 来实现比复制 `for..in` 循环中的属性更强大的对象克隆方式: ```js -let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +let clone = Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); ``` 此调用可以对 `obj` 进行真正准确地拷贝,包括所有的属性:可枚举和不可枚举的,数据属性和 setters/getters —— 包括所有内容,并带有正确的 `[[Prototype]]`。 ## 原型简史 -如果我们数一下有多少种处理 `[[Prototype]]` 的方式,答案是有很多!很多种方法做的都是同一件事儿! - -为什么会出现这种情况? +有这么多可以处理 `[[Prototype]]` 的方式。发生了什么?为什么会这样? 这是历史原因。 -- 构造函数的 `"prototype"` 属性自古以来就起作用。 -- 之后,在 2012 年,`Object.create` 出现在标准中。它提供了使用给定原型创建对象的能力。但没有提供 get/set 它的能力。因此,许多浏览器厂商实现了非标准的 `__proto__` 访问器,该访问器允许用户随时 get/set 原型。 +原型继承从一开始就存在于语言中,但管理它的方式随着时间的推移而演变。 + +- 构造函数的 `"prototype"` 属性自古以来就起作用。这是使用给定原型创建对象的最古老的方式。 +- 之后,在 2012 年,`Object.create` 出现在标准中。它提供了使用给定原型创建对象的能力。但没有提供 get/set 它的能力。一些浏览器实现了非标准的 `__proto__` 访问器,以为开发者提供更多的灵活性。 - 之后,在 2015 年,`Object.setPrototypeOf` 和 `Object.getPrototypeOf` 被加入到标准中,执行与 `__proto__` 相同的功能。由于 `__proto__` 实际上已经在所有地方都得到了实现,但它已过时,所以被加入到该标准的附件 B 中,即:在非浏览器环境下,它的支持是可选的。 +- 之后,在 2022 年,官方允许在对象字面量 `{...}` 中使用 `__proto__`(从附录 B 中移出来了),但不能用作 getter/setter `obj.__proto__`(仍在附录 B 中)。 + +为什么要用函数 `getPrototypeOf/setPrototypeOf` 取代 `__proto__`? -目前为止,我们拥有了所有这些方式。 +为什么 `__proto__` 被部分认可并允许在 `{...}` 中使用,但仍不能用作 getter/setter? -为什么将 `__proto__` 替换成函数 `getPrototypeOf/setPrototypeOf`?这是一个有趣的问题,需要我们理解为什么 `__proto__` 不好。继续阅读,你就会知道答案。 +这是一个有趣的问题,需要我们理解为什么 `__proto__` 不好。 + +很快我们就会看到答案。 ```warn header="如果速度很重要,就请不要修改已存在的对象的 `[[Prototype]]`" 从技术上来讲,我们可以在任何时候 get/set `[[Prototype]]`。但是通常我们只在创建对象的时候设置它一次,自那之后不再修改:`rabbit` 继承自 `animal`,之后不再更改。 @@ -101,25 +114,36 @@ obj[key] = "some value"; alert(obj[key]); // [object Object],并不是 "some value"! ``` -这里如果用户输入 `__proto__`,那么赋值会被忽略! +这里如果用户输入 `__proto__`,那么在第四行的赋值会被忽略! -我们不应该对此感到惊讶。`__proto__` 属性很特别:它必须是对象或者 `null`。字符串不能成为 prototype。 +对于非开发者来说,这肯定很令人惊讶,但对我们来说却是可以理解的。`__proto__` 属性很特殊:它必须是一个对象或者 `null`。字符串不能成为原形。这就是为什么将字符串赋值给 `__proto__` 会被忽略。 -但是我们不是 **打算** 实现这种行为,对吧?我们想要存储键值对,然而键名为 `"__proto__"` 的键值对没有被正确存储。所以这是一个 bug。 +但我们不是 **打算** 实现这种行为,对吧?我们想要存储键值对,然而键名为 `"__proto__"` 的键值对没有被正确存储。所以这是一个 bug。 -在这里,后果并没有很严重。但是在其他情况下,我们可能会对对象进行赋值操作,然后原型可能就被更改了。结果,可能会导致完全意想不到的结果。 +这里的后果并没有很严重。但在其他情况下,我们可能会在 `obj` 中存储对象而不是字符串,则原形确实会被改变。结果,执行将以完全意想不到的方式出错。 最可怕的是 —— 通常开发者完全不会考虑到这一点。这让此类 bug 很难被发现,甚至变成漏洞,尤其是在 JavaScript 被用在服务端的时候。 -即使对在默认情况下为函数的 `toString` 以及其他内建方法执行赋值操作,也会出现意想不到的结果。 +对 `obj.toString` 进行赋值时也可能发生意想不到的事情,因为它是一个内建的对象方法。 我们怎么避免这样的问题呢? -首先,我们可以改用 `Map` 来代替普通对象进行存储,这样一切都迎刃而解。 +首先,我们可以改用 `Map` 来代替普通对象进行存储,这样一切都迎刃而解: -但是 `Object` 在这里同样可以运行得很好,因为 JavaScript 语言的制造者很早就注意到了这个问题。 +```js run +let map = new Map(); -`__proto__` 不是一个对象的属性,只是 `Object.prototype` 的访问器属性: +let key = prompt("What's the key?", "__proto__"); +map.set(key, "some value"); + +alert(map.get(key)); // "some value"(符合预期) +``` + +……但 `Object` 语法通常更吸引人,因为它更简洁。 + +幸运的是,我们 **可以** 使用对象,因为 JavaScript 语言的制造者很久以前就考虑过这个问题。 + +正如我们所知道的,`__proto__` 不是对象的属性,而是 `Object.prototype` 的访问器属性: ![](object-prototype-2.svg) @@ -132,6 +156,7 @@ alert(obj[key]); // [object Object],并不是 "some value"! ```js run *!* let obj = Object.create(null); +// 或者:obj = { __proto__: null } */!* let key = prompt("What's the key?", "__proto__"); @@ -173,32 +198,26 @@ alert(Object.keys(chineseDictionary)); // hello,bye ## 总结 -设置和直接访问原型的现代方法有: - -- [Object.create(proto, [descriptors])](mdn:js/Object/create) —— 利用给定的 `proto` 作为 `[[Prototype]]`(可以是 `null`)和可选的属性描述来创建一个空对象。 -- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) —— 返回对象 `obj` 的 `[[Prototype]]`(与 `__proto__` 的 getter 相同)。 -- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) —— 将对象 `obj` 的 `[[Prototype]]` 设置为 `proto`(与 `__proto__` 的 setter 相同)。 +- 要使用给定的原型创建对象,使用: -如果要将一个用户生成的键放入一个对象,那么内建的 `__proto__` getter/setter 是不安全的。因为用户可能会输入 `"__proto__"` 作为键,这会导致一个 error,虽然我们希望这个问题不会造成什么大影响,但通常会造成不可预料的后果。 + - 字面量语法:`{ __proto__: ... }`,允许指定多个属性 + - 或 [Object.create(proto, [descriptors])](mdn:js/Object/create),允许指定属性描述符。 -因此,我们可以使用 `Object.create(null)` 创建一个没有 `__proto__` 的 "very plain" 对象,或者对此类场景坚持使用 `Map` 对象就可以了。 + `Object.create` 提供了一种简单的方式来浅拷贝对象及其所有属性描述符(descriptors)。 -此外,`Object.create` 提供了一种简单的方式来浅拷贝一个对象的所有描述符: + ```js + let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); + ``` -```js -let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); -``` +- 设置和访问原型的现代方法有: -此外,我们还明确了 `__proto__` 是 `[[Prototype]]` 的 getter/setter,就像其他方法一样,它位于 `Object.prototype`。 + - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) —— 返回对象 `obj` 的 `[[Prototype]]`(与 `__proto__` 的 getter 相同)。 + - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) —— 将对象 `obj` 的 `[[Prototype]]` 设置为 `proto`(与 `__proto__` 的 setter 相同)。 -我们可以通过 `Object.create(null)` 来创建没有原型的对象。这样的对象被用作 "pure dictionaries",对于它们而言,使用 `"__proto__"` 作为键是没有问题的。 +- 不推荐使用内建的的 `__proto__` getter/setter 获取/设置原型,它现在在 ECMA 规范的附录 B 中。 -其他方法: +- 我们还介绍了使用 `Object.create(null)` 或 `{__proto__: null}` 创建的无原型的对象。 -- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) —— 返回一个可枚举的由自身的字符串属性名/值/键值对组成的数组。 -- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) —— 返回一个由自身所有的 symbol 类型的键组成的数组。 -- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) —— 返回一个由自身所有的字符串键组成的数组。 -- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) —— 返回一个由自身所有键组成的数组。 -- [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty):如果 `obj` 拥有名为 `key` 的自身的属性(非继承而来的),则返回 `true`。 + 这些对象被用作字典,以存储任意(可能是用户生成的)键。 -所有返回对象属性的方法(如 `Object.keys` 及其他)—— 都返回“自身”的属性。如果我们想继承它们,我们可以使用 `for...in`。 + 通常,对象会从 `Object.prototype` 继承内建的方法和 `__proto__` getter/setter,会占用相应的键,且可能会导致副作用。原型为 `null` 时,对象才真正是空的。 diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md index f2305b36d3..6f2e4f7488 100644 --- a/1-js/09-classes/02-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -106,7 +106,7 @@ class Rabbit extends Animal { } ``` -但是通常来说,我们不希望完全替换父类的方法,而是希望在父类方法的基础上进行调整或扩展其功能。我们在我们的方法中做一些事儿,但是在它之前或之后或在过程中会调用父类方法。 +然而通常,我们不希望完全替换父类的方法,而是希望在父类方法的基础上进行调整或扩展其功能。我们在我们的方法中做一些事儿,但是在它之前或之后或在过程中会调用父类方法。 Class 为此提供了 `"super"` 关键字。 @@ -160,6 +160,7 @@ rabbit.stop(); // White Rabbit stands still. White Rabbit hides! 正如我们在 一章中所提到的,箭头函数没有 `super`。 如果被访问,它会从外部函数获取。例如: + ```js class Rabbit extends Animal { stop() { @@ -176,7 +177,6 @@ setTimeout(function() { super.stop() }, 1000); ``` ```` - ## 重写 constructor 对于重写 constructor 来说,则有点棘手。 @@ -280,8 +280,6 @@ alert(rabbit.earLength); // 10 */!* ``` - - ### 重写类字段: 一个棘手的注意要点 ```warn header="高阶要点" @@ -376,7 +374,6 @@ new Rabbit(); // rabbit 如果出问题了,我们可以通过使用方法或者 getter/setter 替代类字段,来修复这个问题。 - ## 深入:内部探究和 [[HomeObject]] ```warn header="进阶内容" diff --git a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md b/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md index 7bacb2e9e9..56569a6958 100644 --- a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md +++ b/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md @@ -21,14 +21,14 @@ alert( rabbit.hasOwnProperty('name') ); // true 但这还不是全部原因。 -即便修复了它,`"class Rabbit extends Object"` 和 `class Rabbit` 之间仍存在着重要差异。 +即便修复了它,`"class Rabbit extends Object"` 和 `class Rabbit` 之间仍存在着一个重要的差异。 我们知道,"extends" 语法会设置两个原型: 1. 在构造函数的 `"prototype"` 之间设置原型(为了获取实例方法)。 2. 在构造函数之间会设置原型(为了获取静态方法)。 -在我们的例子里,对于 `class Rabbit extends Object`,它意味着: +在 `class Rabbit extends Object` 的例子中,意味着: ```js run class Rabbit extends Object {} @@ -67,7 +67,7 @@ alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error 所以,在这种情况下,`Rabbit` 没有提供对 `Object` 的静态方法的访问。 -顺便说一下,`Function.prototype` 有一些“通用”函数方法,例如 `call` 和 `bind` 等。在上述的两种情况下它们都是可用的,因为对于内建的 `Object` 构造函数而言,`Object.__proto__ === Function.prototype`。 +顺便说一下,`Function.prototype` 也有一些“通用”函数方法,例如 `call` 和 `bind` 等。在上述的两种情况下它们都是可用的,因为对于内建的 `Object` 构造函数而言,`Object.__proto__ === Function.prototype`。 我们用一张图来解释: diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md index 403134f407..d173501b26 100644 --- a/1-js/09-classes/03-static-properties-methods/article.md +++ b/1-js/09-classes/03-static-properties-methods/article.md @@ -109,6 +109,17 @@ alert( article.title ); // Today's digest Article.remove({id: 12345}); ``` +````warn header="静态方法不适用于单个对象" +静态方法可以在类上调用,而不是在单个对象上。 + +例如,这样的代码无法正常工作: + +```js +// ... +article.createTodays(); /// Error: article.createTodays is not a function +``` +```` + ## 静态属性 [recent browser=Chrome] diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md index 6a5115eeee..d98e2a69e6 100644 --- a/1-js/11-async/01-callbacks/article.md +++ b/1-js/11-async/01-callbacks/article.md @@ -77,6 +77,8 @@ function loadScript(src, *!*callback*/!*) { } ``` +`onload` 事件在 一文中有描述,它通常会在脚本加载和执行完成后执行一个函数。 + 现在,如果我们想调用该脚本中的新函数,我们应该将其写在回调函数中: ```js @@ -102,7 +104,7 @@ function loadScript(src, callback) { *!* loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => { alert(`Cool, the script ${script.src} is loaded`); - alert( _ ); // 所加载的脚本中声明的函数 + alert( _ ); // _ 是所加载的脚本中声明的一个函数 }); */!* ``` diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md index 8547c2ca15..d27b2bf549 100644 --- a/1-js/11-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -127,9 +127,9 @@ let promise = new Promise(function(resolve, reject) { Promise 对象的 `state` 和 `result` 属性都是内部的。我们无法直接访问它们。但我们可以对它们使用 `.then`/`.catch`/`.finally` 方法。我们在下面对这些方法进行了描述。 ``` -## 消费者:then,catch,finally +## 消费者:then,catch -Promise 对象充当的是 executor(“生产者代码”或“歌手”)和消费函数(“粉丝”)之间的连接,后者将接收结果或 error。可以通过使用 `.then`、`.catch` 和 `.finally` 方法为消费函数进行注册。 +Promise 对象充当的是 executor(“生产者代码”或“歌手”)和消费函数(“粉丝”)之间的连接,后者将接收结果或 error。可以通过使用 `.then` 和 `.catch` 方法注册消费函数。 ### then @@ -144,9 +144,9 @@ promise.then( ); ``` -`.then` 的第一个参数是一个函数,该函数将在 promise resolved 后运行并接收结果。 +`.then` 的第一个参数是一个函数,该函数将在 promise resolved 且接收到结果后执行。 -`.then` 的第二个参数也是一个函数,该函数将在 promise rejected 后运行并接收 error。 +`.then` 的第二个参数也是一个函数,该函数将在 promise rejected 且接收到 error 信息后执行。 例如,以下是对成功 resolved 的 promise 做出的反应: @@ -212,59 +212,83 @@ promise.catch(alert); // 1 秒后显示 "Error: Whoops!" `.catch(f)` 调用是 `.then(null, f)` 的完全的模拟,它只是一个简写形式。 -### finally +## 清理:finally 就像常规 `try {...} catch {...}` 中的 `finally` 子句一样,promise 中也有 `finally`。 -`.finally(f)` 调用与 `.then(f, f)` 类似,在某种意义上,`f` 总是在 promise 被 settled 时运行:即 promise 被 resolve 或 reject。 +调用 `.finally(f)` 类似于 `.then(f, f)`,因为当 promise settled 时 `f` 就会执行:无论 promise 被 resolve 还是 reject。 -`finally` 是执行清理(cleanup)的很好的处理程序(handler),例如无论结果如何,都停止使用不再需要的加载指示符(indicator)。 +`finally` 的功能是设置一个处理程序在前面的操作完成后,执行清理/终结。 -像这样: +例如,停止加载指示器,关闭不再需要的连接等。 + +把它想象成派对的终结者。无论派对是好是坏,有多少朋友参加,我们都需要(或者至少应该)在它之后进行清理。 + +代码可能看起来像这样: ```js new Promise((resolve, reject) => { - /* 做一些需要时间的事儿,然后调用 resolve/reject */ + /* 做一些需要时间的事儿,之后调用可能会 resolve 也可能会 reject */ }) *!* // 在 promise 为 settled 时运行,无论成功与否 .finally(() => stop loading indicator) - // 所以,加载指示器(loading indicator)始终会在我们处理结果/错误之前停止 + // 所以,加载指示器(loading indicator)始终会在我们继续之前停止 */!* .then(result => show result, err => show error) ``` -也就是说,`finally(f)` 其实并不是 `then(f,f)` 的别名。它们之间有一些细微的区别: +请注意,`finally(f)` 并不完全是 `then(f,f)` 的别名。 + +它们之间有重要的区别: + +1. `finally` 处理程序(handler)没有参数。在 `finally` 中,我们不知道 promise 是否成功。没关系,因为我们的任务通常是执行“常规”的完成程序(finalizing procedures)。 -1. `finally` 处理程序(handler)没有参数。在 `finally` 中,我们不知道 promise 是否成功。没关系,因为我们的任务通常是执行“常规”的定稿程序(finalizing procedures)。 -2. `finally` 处理程序将结果和 error 传递给下一个处理程序。 + 请看上面的例子:如你所见,`finally` 处理程序没有参数,promise 的结果由下一个处理程序处理。 +2. `finally` 处理程序将结果或 errir “传递”给下一个合适的处理程序。 例如,在这儿结果被从 `finally` 传递给了 `then`: + ```js run new Promise((resolve, reject) => { - setTimeout(() => resolve("result"), 2000) + setTimeout(() => resolve("value"), 2000) }) - .finally(() => alert("Promise ready")) - .then(result => alert(result)); // <-- .then 对结果进行处理 + .finally(() => alert("Promise ready")) // 先触发 + .then(result => alert(result)); // <-- .then 显示 "value" ``` - 在这儿,promise 中有一个 error,这个 error 被从 `finally` 传递给了 `catch`: + 正如我们所看到的,第一个 promise 返回的 `value` 通过 `finally` 被传递给了下一个 `then`。 + + 这非常方便,因为 `finally` 并不意味着处理一个 promise 的结果。如前所述,无论结果是什么,它都是进行常规清理的地方。 + + 下面是一个 promise 返回结果为 error 的示例,让我们看看它是如何通过 `finally` 被传递给 `catch` 的: ```js run new Promise((resolve, reject) => { throw new Error("error"); }) - .finally(() => alert("Promise ready")) - .catch(err => alert(err)); // <-- .catch 对 error 对象进行处理 + .finally(() => alert("Promise ready")) // 先触发 + .catch(err => alert(err)); // <-- .catch 显示这个 error ``` -这非常方便,因为 `finally` 并不是意味着要处理 promise 的结果。所以它将结果传递了下去。 +3. `finally` 处理程序也不应该返回任何内容。如果它返回了,返回的值会默认被忽略。 + + 此规则的唯一例外是当 `finally` 处理程序抛出 error 时。此时这个 error(而不是任何之前的结果)会被转到下一个处理程序。 -在下一章中,我们将详细讨论 promise 链以及处理程序(handler)之间的结果传递。 +总结: +- `finally` 处理程序没有得到前一个处理程序的结果(它没有参数)。而这个结果被传递给了下一个合适的处理程序。 +- 如果 `finally` 处理程序返回了一些内容,那么这些内容会被忽略。 +- 当 `finally` 抛出 error 时,执行将转到最近的 error 的处理程序。 + +如果我们正确使用 `finally`(将其用于常规清理),那么这些功能将很有用。 ````smart header="我们可以对 settled 的 promise 附加处理程序" -如果 promise 为 pending 状态,`.then/catch/finally` 处理程序(handler)将等待它。否则,如果 promise 已经是 settled 状态,它们就会运行: +如果 promise 为 pending 状态,`.then/catch/finally` 处理程序(handler)将等待它的结果。 + +有时候,当我们向一个 promise 添加处理程序时,它可能已经 settled 了。 + +在这种情况下,这些处理程序会立即执行: ```js run // 下面这 promise 在被创建后立即变为 resolved 状态 @@ -278,10 +302,10 @@ promise.then(alert); // done!(现在显示) Promise 则更加灵活。我们可以随时添加处理程序(handler):如果结果已经在了,它们就会执行。 ```` -接下来,让我们看一下关于 promise 如何帮助我们编写异步代码的更多实际示例。 - ## 示例:loadScript [#loadscript] +接下来,让我们看一下关于 promise 如何帮助我们编写异步代码的更多实际示例。 + 我们从上一章获得了用于加载脚本的 `loadScript` 函数。 这是基于回调函数的变体,记住它: diff --git a/1-js/11-async/04-promise-error-handling/article.md b/1-js/11-async/04-promise-error-handling/article.md index a0344edd2f..141c7a4d84 100644 --- a/1-js/11-async/04-promise-error-handling/article.md +++ b/1-js/11-async/04-promise-error-handling/article.md @@ -199,6 +199,7 @@ new Promise(function() { ## 总结 - `.catch` 处理 promise 中的各种 error:在 `reject()` 调用中的,或者在处理程序(handler)中抛出的(thrown)error。 +- 如果给定 `.then` 的第二个参数(即 error 处理程序),那么 `.then` 也会以相同的方式捕获 error。 - 我们应该将 `.catch` 准确地放到我们想要处理 error,并知道如何处理这些 error 的地方。处理程序应该分析 error(可以自定义 error 类来帮助分析)并再次抛出未知的 error(可能它们是编程错误)。 - 如果没有办法从 error 中恢复的话,不使用 `.catch` 也可以。 - 在任何情况下我们都应该有 `unhandledrejection` 事件处理程序(用于浏览器,以及其他环境的模拟),以跟踪未处理的 error 并告知用户(可能还有我们的服务器)有关信息,以使我们的应用程序永远不会“死掉”。 diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md index 197a9cf7a2..36d6479ce3 100755 --- a/1-js/13-modules/01-modules-intro/article.md +++ b/1-js/13-modules/01-modules-intro/article.md @@ -225,6 +225,7 @@ import { sayHi } from './admin.js'; sayHi(); // Ready to serve, *!*Pete*/!*! ``` + ### import.meta `import.meta` 对象包含关于当前模块的信息。 @@ -233,7 +234,8 @@ sayHi(); // Ready to serve, *!*Pete*/!*! ```html run height=0 ``` diff --git a/1-js/99-js-misc/01-proxy/proxy.svg b/1-js/99-js-misc/01-proxy/proxy.svg index 157e350f44..6b2224cfdc 100644 --- a/1-js/99-js-misc/01-proxy/proxy.svg +++ b/1-js/99-js-misc/01-proxy/proxy.svg @@ -1 +1 @@ -test: 5proxytargetget proxy.test5 \ No newline at end of file +test: 5proxytargetget proxy.test5 \ No newline at end of file diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index 99caf9de69..6c27dd612a 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,6 +1,6 @@ # 浏览器环境,规格 -JavaScript 语言最初是为 Web 浏览器创建的。此后,它已经发展成为一种具有多种用途和平台的语言。 +JavaScript 语言最初是为 Web 浏览器创建的。此后,它已经演变成了一种具有多种用途和平台的语言。 平台可以是一个浏览器,一个 Web 服务器,或其他 **主机(host)**,甚至可以是一个“智能”咖啡机,如果它能运行 JavaScript 的话。它们每个都提供了特定于平台的功能。JavaScript 规范将其称为 **主机环境**。 @@ -15,7 +15,7 @@ JavaScript 语言最初是为 Web 浏览器创建的。此后,它已经发展 1. 首先,它是 JavaScript 代码的全局对象,如 一章所述。 2. 其次,它代表“浏览器窗口”,并提供了控制它的方法。 -例如,在这里我们将它用作全局对象: +例如,我们可以将它用作全局对象: ```js run global function sayHi() { @@ -26,13 +26,13 @@ function sayHi() { window.sayHi(); ``` -在这里,我们将它用作浏览器窗口,以查看窗口高度: +并且我们可以将它用作浏览器窗口,以查看窗口高度: ```js run alert(window.innerHeight); // 内部窗口高度 ``` -还有更多窗口特定的方法和属性,我们稍后会介绍它们。 +还有更多窗口特定的方法和属性,我们稍后会介绍。 ## 文档对象模型(DOM) @@ -69,7 +69,7 @@ DOM 规范解释了文档的结构,并提供了操作文档的对象。有的 例如: -- [navigator](mdn:api/Window/navigator) 对象提供了有关浏览器和操作系统的背景信息。navigator 有许多属性,但是最广为人知的两个属性是:`navigator.userAgent` — 关于当前浏览器,`navigator.platform` — 关于平台(可以帮助区分 Windows/Linux/Mac 等)。 +- [navigator](mdn:api/Window/navigator) 对象提供了有关浏览器和操作系统的背景信息。navigator 有许多属性,但是最广为人知的两个属性是:`navigator.userAgent` — 关于当前浏览器,`navigator.platform` — 关于平台(有助于区分 Windows/Linux/Mac 等)。 - [location](mdn:api/Window/location) 对象允许我们读取当前 URL,并且可以将浏览器重定向到新的 URL。 这是我们可以如何使用 `location` 对象的方法: @@ -106,7 +106,7 @@ HTML 规范 请注意这些链接,因为要学的东西太多了,所以不可能涵盖并记住所有内容。 -当你想要了解某个属性或方法时,Mozilla 手册 是一个很好的资源,但对应的规范可能会更好:它更复杂,且阅读起来需要更长的时间,但是会使你的基本知识更加全面,更加完整。 +当你想要了解某个属性或方法时,Mozilla 手册 是一个很好的资源,但对应的规范可能会更好:它更复杂,且阅读起来需要更长的时间,但是会使你的基本知识更加全面,更加完整。 要查找某些内容时,你通常可以使用互联网搜索 "WHATWG [term]" 或 "MDN [term]",例如 。 diff --git a/2-ui/1-document/02-dom-nodes/domconsole0.svg b/2-ui/1-document/02-dom-nodes/domconsole0.svg index ea0d9141c9..eb99f193fe 100644 --- a/2-ui/1-document/02-dom-nodes/domconsole0.svg +++ b/2-ui/1-document/02-dom-nodes/domconsole0.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/2-ui/1-document/02-dom-nodes/domconsole1.svg b/2-ui/1-document/02-dom-nodes/domconsole1.svg index d7f32debba..02ef5f0a65 100644 --- a/2-ui/1-document/02-dom-nodes/domconsole1.svg +++ b/2-ui/1-document/02-dom-nodes/domconsole1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/2-ui/1-document/02-dom-nodes/elk.svg b/2-ui/1-document/02-dom-nodes/elk.svg index 1797a099ff..448eea9d13 100644 --- a/2-ui/1-document/02-dom-nodes/elk.svg +++ b/2-ui/1-document/02-dom-nodes/elk.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/2-ui/1-document/02-dom-nodes/inspect.svg b/2-ui/1-document/02-dom-nodes/inspect.svg index a894a5c0ea..60696ec0d5 100644 --- a/2-ui/1-document/02-dom-nodes/inspect.svg +++ b/2-ui/1-document/02-dom-nodes/inspect.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index e887e99bab..89e2902293 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -116,7 +116,7 @@ CSS 选择器的伪类,例如 `:hover` 和 `:active` 也都是被支持的。 之前的方法是搜索 DOM。 -[elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) 不会查找任何内容,它只会检查 `elem` 是否与给定的 CSS 选择器匹配。它返回 `true` 或 `false`。 +[elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) 不会查找任何内容,它只会检查 `elem` 是否与给定的 CSS 选择器匹配。它返回 `true` 或 `false`。 当我们遍历元素(例如数组或其他内容)并试图过滤那些我们感兴趣的元素时,这个方法会很有用。 diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index 9b5fc1e6bb..ba25b73edd 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -10,7 +10,7 @@ 每个 DOM 节点都属于相应的内建类。 -层次结构(hierarchy)的根节点是 [EventTarget](https://dom.spec.whatwg.org/#eventtarget),[Node](http://dom.spec.whatwg.org/#interface-node) 继承自它,其他 DOM 节点继承自 Node。 +层次结构(hierarchy)的根节点是 [EventTarget](https://dom.spec.whatwg.org/#eventtarget),[Node](https://dom.spec.whatwg.org/#interface-node) 继承自它,其他 DOM 节点继承自 Node。 下图做了进一步说明: @@ -18,18 +18,39 @@ 类如下所示: -- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) — 是根的“抽象(abstract)”类。该类的对象从未被创建。它作为一个基础,以便让所有 DOM 节点都支持所谓的“事件(event)”,我们会在之后学习它。 -- [Node](http://dom.spec.whatwg.org/#interface-node) — 也是一个“抽象”类,充当 DOM 节点的基础。它提供了树的核心功能:`parentNode`,`nextSibling`,`childNodes` 等(它们都是 getter)。`Node` 类的对象从未被创建。但是有一些继承自它的具体的节点类,例如:文本节点的 `Text`,元素节点的 `Element`,以及更多异域(exotic)类,例如注释节点的 `Comment`。 -- [Element](http://dom.spec.whatwg.org/#interface-element) — 是 DOM 元素的基本类。它提供了元素级的导航(navigation),例如 `nextElementSibling`,`children`,以及像 `getElementsByTagName` 和 `querySelector` 这样的搜索方法。浏览器中不仅有 HTML,还会有 XML 和 SVG。`Element` 类充当更多特定类的基本类:`SVGElement`,`XMLElement` 和 `HTMLElement`。 -- [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) — 最终是所有 HTML 元素的基本类。各种 HTML 元素均继承自它: - - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) — `` 元素的类, - - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) — `` 元素的类, - - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) — `` 元素的类, +- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) —— 是一切的根“抽象(abstract)”类。 + + 该类的对象从未被创建。它作为一个基础,以便让所有 DOM 节点都支持所谓的“事件(event)”,我们会在之后学习它。 + +- [Node](http://dom.spec.whatwg.org/#interface-node) —— 也是一个“抽象”类,充当 DOM 节点的基础。 + + 它提供了树的核心功能:`parentNode`,`nextSibling`,`childNodes` 等(它们都是 getter)。`Node` 类的对象从未被创建。但是还有一些继承自它的其他类(因此继承了 `Node` 的功能)。 + +- [Document](https://dom.spec.whatwg.org/#interface-document) 由于历史原因通常被 `HTMLDocument` 继承(尽管最新的规范没有规定)—— 是一个整体的文档。 + + 全局变量 `document` 就是属于这个类。它作为 DOM 的入口。 + +- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) —— 一个“抽象”类,被下述类继承: + - [Text](https://dom.spec.whatwg.org/#interface-text) —— 对应于元素内部文本的类,例如 `

Hello

` 中的 `Hello`。 + - [Comment](https://dom.spec.whatwg.org/#interface-comment) —— 注释类。它们不会被展示出来,但每个注释都会成为 DOM 中的一员。 + +- [Element](http://dom.spec.whatwg.org/#interface-element) —— 是 DOM 元素的基础类。 + + 它提供了元素级导航(navigation),如 `nextElementSibling`,`children`,以及搜索方法,如 `getElementsByTagName` 和 `querySelector`。 + + 浏览器不仅支持 HTML,还支持 XML 和 SVG。因此,`Element` 类充的是更具体的类的基础:`SVGElement`,`XMLElement`(我们在这里不需要它)和 `HTMLElement`。 + +- 最后,[HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) —— 是所有 HTML 元素的基础类。我们大部分时候都会用到它。 + + 它会被更具体的 HTML 元素继承: + - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) —— `` 元素的类, + - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) —— `` 元素的类, + - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) —— `
` 元素的类, - ……等。 还有很多其他标签具有自己的类,可能还具有特定的属性和方法,而一些元素,如 ``、`
`、`
` 等,没有任何特定的属性,所以它们是 `HTMLElement` 类的实例。 -因此,给定节点的全部属性和方法都是继承的结果。 +因此,给定节点的全部属性和方法都是继承链的结果。 例如,我们考虑一下 `` 元素的 DOM 对象。它属于 [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) 类。 @@ -133,10 +154,10 @@ interface HTMLInputElement: HTMLElement { ``` diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md index aa3604002d..a2a46aaa00 100644 --- a/2-ui/1-document/08-styles-and-classes/article.md +++ b/2-ui/1-document/08-styles-and-classes/article.md @@ -126,7 +126,15 @@ document.body.style.display = "none"; // 隐藏 setTimeout(() => document.body.style.display = "", 1000); // 恢复正常 ``` -如果我们将 `display` 设置为空字符串,那么浏览器通常会应用 CSS 类以及内建样式,就好像根本没有这样的 `style` 属性一样。 +如果我们将 `style.display` 设置为空字符串,那么浏览器通常会应用 CSS 类以及内建样式,就好像根本没有这样的 `style.display` 属性一样。 + +还有一个特殊的方法 `elem.style.removeProperty('style property')`。所以,我们可以像这样删除一个属性: + +```js run +document.body.style.background = 'red'; //将 background 设置为红色 + +setTimeout(() => document.body.style.removeProperty('background'), 1000); // 1 秒后移除 background +``` ````smart header="用 `style.cssText` 进行完全的重写" 通常,我们使用 `style.*` 来对各个样式属性进行赋值。我们不能像这样的 `div.style="color: red; width: 100px"` 设置完整的属性,因为 `div.style` 是一个对象,并且它是只读的。 diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg index 4ae90b1c71..f5bd9f4f9c 100644 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg @@ -1 +1 @@ -(0,0)clientWidth \ No newline at end of file +(0,0)clientWidth \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-all.svg b/2-ui/1-document/09-size-and-scroll/metric-all.svg index a5dadb47f0..20a59e18d7 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-all.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-all.svg @@ -1 +1 @@ -Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollHeightoffsetHeightscrollTopclientHeightoffsetTopclientLeftclientWidthclientTopoffsetLeftoffsetWidth \ No newline at end of file +Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollHeightoffsetHeightscrollTopclientHeightoffsetTopclientLeftclientWidthclientTopoffsetLeftoffsetWidth \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg index 83864b4c57..2603b05fba 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg @@ -1 +1 @@ -border 25pxpadding 20pxcontent width: 284pxborder 25pxpadding 20pxscrollbar 16pxclientWidth = 20+284+20 = 324pxclientHeight: 240pxheight: 200pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with \ No newline at end of file +border 25pxpadding 20pxcontent width: 284pxborder 25pxpadding 20pxscrollbar 16pxclientWidth = 20+284+20 = 324pxclientHeight: 240pxheight: 200pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-css.svg b/2-ui/1-document/09-size-and-scroll/metric-css.svg index 13aa62afd0..1f2e5f780e 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-css.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-css.svg @@ -1 +1 @@ -padding: 20pxheight: 200pxpadding: 20pxborder 25pxpadding 20pxcontent width: 284pxborder 25pxpadding 20pxscrollbar 16pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with \ No newline at end of file +padding: 20pxheight: 200pxpadding: 20pxborder 25pxpadding 20pxcontent width: 284pxborder 25pxpadding 20pxscrollbar 16pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg index 9e247639be..2d108473ee 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg @@ -1 +1 @@ -offsetTop: 180pxoffsetLeft: 180pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoftposition: absolute; left: 180px; top: 180px;offsetParent <MAIN> <DIV> \ No newline at end of file +offsetTop: 180pxoffsetLeft: 180pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoftposition: absolute; left: 180px; top: 180px;offsetParent <MAIN> <DIV> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg index 49bdccda78..4d30d90cc8 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg @@ -1 +1 @@ -border 25pxpadding 20pxcontent width: 284pxheight: 200pxborder 25pxpadding 20pxscrollbar 16pxoffsetWidth = 25+20+284+20+16+25 = 390pxoffsetHeight: 290pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with \ No newline at end of file +border 25pxpadding 20pxcontent width: 284pxheight: 200pxborder 25pxpadding 20pxscrollbar 16pxoffsetWidth = 25+20+284+20+16+25 = 390pxoffsetHeight: 290pxIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg index c6d14d0f38..7f72de422a 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg @@ -1 +1 @@ -Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollTopscrollHeight: 723px \ No newline at end of file +Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollTopscrollHeight: 723px \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg index 0c3d299528..75a24e3bc3 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg @@ -1 +1 @@ -Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollHeight: 723pxscrollWidth = 324px \ No newline at end of file +Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollHeight: 723pxscrollWidth = 324px \ No newline at end of file diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg index 65e77ae805..18cd37a749 100644 --- a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg +++ b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg @@ -1 +1 @@ -documentElement.clientHeightdocumentElement.clientWidth \ No newline at end of file +documentElement.clientHeightdocumentElement.clientWidth \ No newline at end of file diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg index fc26b023c5..2acc6b03ec 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg @@ -1 +1 @@ -ball.style.left?fieldCoords.leftevent.clientX \ No newline at end of file +ball.style.left?fieldCoords.leftevent.clientX \ No newline at end of file diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index afab4a5159..b277b16288 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -1,3 +1,4 @@ + # 鼠标事件 在本章中,我们将详细介绍鼠标事件及其属性。 diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html index 3f99f3dc1b..6c70ea98cb 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html @@ -54,7 +54,7 @@

从前有一个猪妈妈,她养了三只小猪。

-

三只小猪长得很快快,妈妈对它们说:“你们太大了,不能住在这里了,你们自己去盖房子吧,但要小心不要让狼抓到你们。

+

三只小猪长得很快快,妈妈对它们说:“你们太大了,不能住在这里了,你们自己去盖房子吧,但要小心不要让狼抓到你们。”

三只小猪出发了。 “我们会注意不要让狼抓住我们,”他们说。

diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html index 54fadab881..17a9eb2509 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html @@ -54,7 +54,7 @@

从前有一个猪妈妈,她养了三只小猪。

-

三只小猪长得很快快,妈妈对它们说:“你们太大了,不能住在这里了,你们自己去盖房子吧,但要小心不要让狼抓到你们。

+

三只小猪长得很快快,妈妈对它们说:“你们太大了,不能住在这里了,你们自己去盖房子吧,但要小心不要让狼抓到你们。”

三只小猪出发了。 “我们会注意不要让狼抓住我们,”他们说。

diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg index 4ae90b1c71..f5bd9f4f9c 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg @@ -1 +1 @@ -(0,0)clientWidth \ No newline at end of file +(0,0)clientWidth \ No newline at end of file diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index 5e014dec87..114ca6e71c 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -90,6 +90,8 @@ Your email please: 请注意,我们无法通过在 `onblur` 事件处理程序中调用 `event.preventDefault()` 来“阻止失去焦点”,因为 `onblur` 事件处理程序是在元素失去焦点 **之后** 运行的。 +但在实际中,在实现这样的功能之前应该认真考虑一下,因为我们通常 **应该将报错展示给用户**,但 **不应该阻止用户在填写我们的表单时的进度**。用户可能会想先填写其他表单项。 + ```warn header="JavaScript 导致的焦点丢失" 很多种原因可以导致焦点丢失。 diff --git a/2-ui/4-forms-controls/3-events-change-input/article.md b/2-ui/4-forms-controls/3-events-change-input/article.md index b8474af9f1..42c0518d26 100644 --- a/2-ui/4-forms-controls/3-events-change-input/article.md +++ b/2-ui/4-forms-controls/3-events-change-input/article.md @@ -101,7 +101,7 @@ 重申,[event.clipboardData](https://www.w3.org/TR/clipboard-apis/#clipboardevent-clipboarddata) 仅在用户启动的事件处理程序的上下文中生效。 -另外, [navigator.clipboard](https://www.w3.org/TR/clipboard-apis/#h-navigator-clipboard) 是一个较新的 API,适用于任何上下文。如果需要,它会请求用户的许可。火狐(Firefox)浏览器尚未支持。 +另外, [navigator.clipboard](https://www.w3.org/TR/clipboard-apis/#h-navigator-clipboard) 是一个较新的 API,适用于任何上下文。如果需要,它会请求用户的许可。 ## 总结 diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg index 9ebcffaac4..a97d1b47ad 100644 --- a/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg @@ -1 +1 @@ -0123 \ No newline at end of file +0123 \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg index 088c71c208..2a8f9aca32 100644 --- a/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg @@ -1 +1 @@ -0123 \ No newline at end of file +0123 \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg index f13c6d74a7..32843436d8 100644 --- a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg @@ -1 +1 @@ -startContainer (<p>.firstChild)startOffset (=2)commonAncestorContainer (<p>)endContainer (<b>.firstChild)endOffset (=3) \ No newline at end of file +startContainer (<p>.firstChild)startOffset (=2)commonAncestorContainer (<p>)endContainer (<b>.firstChild)endOffset (=3) \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg index 4bf5b00b00..859f755ce1 100644 --- a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg @@ -1 +1 @@ -0123 \ No newline at end of file +0123 \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg b/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg index 6399f9d5ee..85615d38ff 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg @@ -1 +1 @@ -focusanchormouse move direction \ No newline at end of file +focusanchormouse move direction \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg b/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg index 03c6fc5c61..511b00a261 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg @@ -1 +1 @@ -anchorfocusmouse move direction \ No newline at end of file +anchorfocusmouse move direction \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg index 050852d3df..aa7ff1eb73 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg @@ -1 +1 @@ -selection \ No newline at end of file +selection \ No newline at end of file diff --git a/4-binary/01-arraybuffer-binary-arrays/article.md b/4-binary/01-arraybuffer-binary-arrays/article.md index 785ce65e57..8b3346f2e1 100644 --- a/4-binary/01-arraybuffer-binary-arrays/article.md +++ b/4-binary/01-arraybuffer-binary-arrays/article.md @@ -71,7 +71,7 @@ for(let num of view) { ## TypedArray -所有这些视图(`Uint8Array`,`Uint32Array` 等)的通用术语是 [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects)。它们都享有同一组方法和属性。 +所有这些视图(`Uint8Array`,`Uint32Array` 等)的通用术语是 [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects)。它们共享同一方法和属性集。 请注意,没有名为 `TypedArray` 的构造器,它只是表示 `ArrayBuffer` 上的视图之一的通用总称术语:`Int8Array`,`Uint8Array` 及其他,很快就会有完整列表。 diff --git a/4-binary/03-blob/article.md b/4-binary/03-blob/article.md index 377ea55174..77a83bfad1 100644 --- a/4-binary/03-blob/article.md +++ b/4-binary/03-blob/article.md @@ -237,8 +237,8 @@ const readableStream = blob.stream(); const stream = readableStream.getReader(); while (true) { - // 对于每次迭代:data 是下一个 blob 数据片段 - let { done, data } = await stream.read(); + // 对于每次迭代:value 是下一个 blob 数据片段 + let { done, value } = await stream.read(); if (done) { // 读取完毕,stream 里已经没有数据了 console.log('all blob processed.'); @@ -246,7 +246,7 @@ while (true) { } // 对刚从 blob 中读取的数据片段做一些处理 - console.log(data); + console.log(value); } ``` diff --git a/5-network/06-fetch-api/article.md b/5-network/06-fetch-api/article.md index fb2426e355..12627fafac 100644 --- a/5-network/06-fetch-api/article.md +++ b/5-network/06-fetch-api/article.md @@ -50,9 +50,9 @@ let promise = fetch(url, { 通常来说,这个 header 是被自动设置的,并包含了发出请求的页面的 url。在大多数情况下,它一点也不重要,但有时出于安全考虑,删除或缩短它是有意义的。 -**`referer` 选项允许设置任何 `Referer`(在当前域的),或者移除它。** +**`referrer` 选项允许设置任何 `Referer`(在当前域的),或者移除它。** -要不发送 referer,可以将 `referer` 设置为空字符串: +要不发送 referrer,可以将 `referrer` 设置为空字符串: ```js fetch('/page', { *!* diff --git a/5-network/11-websocket/article.md b/5-network/11-websocket/article.md index 1fb0dbb584..81040490e3 100644 --- a/5-network/11-websocket/article.md +++ b/5-network/11-websocket/article.md @@ -1,6 +1,6 @@ # WebSocket -在 [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455) 规范中描述的 `WebSocket` 协议提供了一种在浏览器和服务器之间建立持久连接来交换数据的方法。数据可以作为“数据包”在两个方向上传递,而不会断开连接和其他 HTTP 请求。 +在 [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455) 规范中描述的 `WebSocket` 协议,提供了一种在浏览器和服务器之间建立持久连接来交换数据的方法。数据可以作为“数据包”在两个方向上传递,而无需中段连接也无需额外的 HTTP 请求。 对于需要连续数据交换的服务,例如网络游戏,实时交易系统等,WebSocket 尤其有用。 @@ -88,7 +88,7 @@ Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13 ``` -- `Origin` —— 客户端页面的源,例如 `https://javascript.info`。WebSocket 对象是原生支持跨源的。没有特殊的 header 或其他限制。旧的服务器无法处理 WebSocket,因此不存在兼容性问题。但是 `Origin` header 很重要,因为它允许服务器决定是否使用 WebSocket 与该网站通信。 +- `Origin` —— 客户端页面的源,例如 `https://javascript.info`。WebSocket 对象是原生支持跨源的。没有特殊的 header 或其他限制。旧的服务器无法处理 WebSocket,因此不存在兼容性问题。但 `Origin` header 很重要,因为它允许服务器决定是否使用 WebSocket 与该网站通信。 - `Connection: Upgrade` —— 表示客户端想要更改协议。 - `Upgrade: websocket` —— 请求的协议是 "websocket"。 - `Sec-WebSocket-Key` —— 浏览器随机生成的安全密钥。 @@ -109,7 +109,7 @@ Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= 这里 `Sec-WebSocket-Accept` 是 `Sec-WebSocket-Key`,是使用特殊的算法重新编码的。浏览器使用它来确保响应与请求相对应。 -然后,就使用 WebSocket 协议传输数据,我们很快就会看到它的结构("frames")。它根本不是 HTTP。 +然后,使用 WebSocket 协议传输数据,我们很快就会看到它的结构("frames")。它根本不是 HTTP。 ### 扩展和子协议 @@ -119,7 +119,7 @@ WebSocket 可能还有其他 header,`Sec-WebSocket-Extensions` 和 `Sec-WebSoc - `Sec-WebSocket-Extensions: deflate-frame` 表示浏览器支持数据压缩。扩展与传输数据有关,扩展了 WebSocket 协议的功能。`Sec-WebSocket-Extensions` header 由浏览器自动发送,其中包含其支持的所有扩展的列表。 -- `Sec-WebSocket-Protocol: soap, wamp` 表示我们不仅要传输任何数据,还要传输 [SOAP](http://en.wikipedia.org/wiki/SOAP) 或 WAMP("The WebSocket Application Messaging Protocol")协议中的数据。WebSocket 子协议已经在 [IANA catalogue](http://www.iana.org/assignments/websocket/websocket.xml) 中注册。 +- `Sec-WebSocket-Protocol: soap, wamp` 表示我们不仅要传输任何数据,还要传输 [SOAP](https://en.wikipedia.org/wiki/SOAP) 或 WAMP("The WebSocket Application Messaging Protocol")协议中的数据。WebSocket 子协议已经在 [IANA catalogue](https://www.iana.org/assignments/websocket/websocket.xml) 中注册。因此,此 header 描述了我们将要使用的数据格式。 这个可选的 header 是使用 `new WebSocket` 的第二个参数设置的。它是子协议数组,例如,如果我们想使用 SOAP 或 WAMP: diff --git a/5-network/11-websocket/websocket-handshake.svg b/5-network/11-websocket/websocket-handshake.svg index a8ec2389ab..96c2cd3efb 100644 --- a/5-network/11-websocket/websocket-handshake.svg +++ b/5-network/11-websocket/websocket-handshake.svg @@ -1 +1 @@ -BrowserServerHTTP-request"Hey, server, let's talk WebSocket?"HTTP-response "Okay!"WebSocket protocol \ No newline at end of file +BrowserServerHTTP-request"Hey, server, let's talk WebSocket?"HTTP-response "Okay!"WebSocket protocol \ No newline at end of file diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md index f8946528d3..438a7d0164 100644 --- a/6-data-storage/01-cookie/article.md +++ b/6-data-storage/01-cookie/article.md @@ -282,7 +282,6 @@ Web 服务器使用 `Set-Cookie` header 来设置 cookie。并且,它可以设 有很多这种 cookie 库,所以这些函数只用于演示。虽然它们都能正常使用。 - ### getCookie(name) 获取 cookie 最简短的方式是使用 [正则表达式](info:regular-expressions)。 diff --git a/6-data-storage/02-localstorage/article.md b/6-data-storage/02-localstorage/article.md index c74d9d6bfe..2dacee4209 100644 --- a/6-data-storage/02-localstorage/article.md +++ b/6-data-storage/02-localstorage/article.md @@ -6,7 +6,7 @@ Web 存储对象 `localStorage` 和 `sessionStorage` 允许我们在浏览器上 我们已经有了 cookie。为什么还要其他存储对象呢? -- 与 cookie 不同,Web 存储对象不会随每个请求被发送到服务器。因此,我们可以保存更多数据。大多数浏览器都允许保存至少 2MB 的数据(或更多),并且具有用于配置数据的设置。 +- 与 cookie 不同,Web 存储对象不会随每个请求被发送到服务器。因此,我们可以保存更多数据。大多数现代浏览器都允许保存至少 5MB 的数据(或更多),并且具有用于配置数据的设置。 - 还有一点和 cookie 不同,服务器无法通过 HTTP header 操纵存储对象。一切都是在 JavaScript 中完成的。 - 存储绑定到源(域/协议/端口三者)。也就是说,不同协议或子域对应不同的存储对象,它们之间无法访问彼此数据。 diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index 392867bdcb..319d7086d4 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -191,10 +191,9 @@ IndexedDB 使用 [标准序列化算法](https://www.w3.org/TR/html53/infrastruc 键的类型必须为数字、日期、字符串、二进制或数组。它是唯一的标识符,所以我们可以通过键来搜索/删除/更新值。 - ![](indexeddb-structure.svg) -类似于 `localStorage`,我们向存储区添加值时,可以提供一个键。但当我们存储对象时,IndexedDB 允许将一个对象属性设置为键,这就更加方便了。或者,我们可以自动生成键。 +正如我们很快就会看到的,类似于 `localStorage`,我们向存储区添加值时,可以提供一个键。但当我们存储对象时,IndexedDB 允许将一个对象属性设置为键,这就更加方便了。或者,我们可以自动生成键。 但我们需要先创建一个对象库。 @@ -213,7 +212,8 @@ db.createObjectStore(name[, keyOptions]); 如果我们不提供 `keyOptions`,那么以后需要在存储对象时,显式地提供一个键。 -例如,此对象库使用 `id` 属性作为键: +例如,此对象库使用 `id` 属性作为键: + ```js db.createObjectStore('books', {keyPath: 'id'}); ``` @@ -223,6 +223,7 @@ db.createObjectStore('books', {keyPath: 'id'}); 这是技术上的限制。在 upgradeneedHandler 之外,可以添加/删除/更新数据,但是只能在版本更新期间创建/删除/更改对象库。 要进行数据库版本升级,主要有两种方法: + 1. 我们实现每个版本的升级功能:从 1 到 2,从 2 到 3,从 3 到 4,等等。在 `upgradeneeded` 中,可以进行版本比较(例如,老版本是 2,需要升级到 4),并针对每个中间版本(2 到 3,然后 3 到 4)逐步运行每个版本的升级。 2. 或者我们可以检查数据库:以 `db.objectStoreNames` 的形式获取现有对象库的列表。该对象是一个 [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) 提供 `contains(name)` 方法来检查 name 是否存在,再根据存在和不存在的内容进行更新。 @@ -242,7 +243,6 @@ openRequest.onupgradeneeded = function() { }; ``` - 删除对象库: ```js @@ -256,6 +256,7 @@ db.deleteObjectStore('books') 事务是一组操作,要么全部成功,要么全部失败。 例如,当一个人买东西时,我们需要: + 1. 从他们的账户中扣除这笔钱。 2. 将该项目添加到他们的清单中。 @@ -276,7 +277,7 @@ db.transaction(store[, type]); - `readonly` —— 只读,默认值。 - `readwrite` —— 只能读取和写入数据,而不能 创建/删除/更改 对象库。 -还有 `versionchange` 事务类型:这种事务可以做任何事情,但不能被手动创建。IndexedDB 在打开数据库时,会自动为 `updateneeded` 处理程序创建 `versionchange` 事务。这就是它为什么可以更新数据库结构、创建/删除 对象库的原因。 +还有 `versionchange` 事务类型:这种事务可以做任何事情,但不能被手动创建。IndexedDB 在打开数据库时,会自动为 `upgradeneeded` 处理程序创建 `versionchange` 事务。这就是它为什么可以更新数据库结构、创建/删除 对象库的原因。 ```smart header="为什么会有不同类型的事务?" 性能是事务需要标记为 `readonly` 和 `readwrite` 的原因。 @@ -597,6 +598,7 @@ request.onsuccess = function() { } }; ``` + 我们还可以使用 `IDBKeyRange` 创建范围,并查找 便宜/贵 的书: ```js @@ -613,6 +615,7 @@ let request = priceIndex.getAll(IDBKeyRange.upperBound(5)); - **`delete(query)`** —— 通过查询删除匹配的值。 例如: + ```js // 删除 id='js' 的书 books.delete('js'); @@ -631,6 +634,7 @@ request.onsuccess = function() { ``` 删除所有内容: + ```js books.clear(); // 清除存储。 ``` @@ -649,7 +653,7 @@ books.clear(); // 清除存储。 由于对象库是按键在内部排序的,因此光标按键顺序(默认为升序)遍历存储。 -语法: +语法: ```js // 类似于 getAll,但带有光标: @@ -748,7 +752,6 @@ try { } catch(err) { console.log('error', err.message); } - ``` 现在我们有了可爱的“简单异步代码”和「try..catch」捕获的东西。 @@ -771,10 +774,8 @@ window.addEventListener('unhandledrejection', event => { ### “非活跃事务”陷阱 - 我们都知道,浏览器一旦执行完成当前的代码和 **微任务** 之后,事务就会自动提交。因此,如果我们在事务中间放置一个类似 `fetch` 的宏任务,事务只是会自动提交,而不会等待它执行完成。因此,下一个请求会失败。 - 对于 promise 包装器和 `async/await`,情况是相同的。 这是在事务中间进行 `fetch` 的示例: @@ -793,6 +794,7 @@ await inventory.add({ id: 'js', price: 10, created: new Date() }); // 错误 `fetch` `(*)` 后的下一个 `inventory.add` 失败,出现“非活动事务”错误,因为这时事务已经被提交并且关闭了。 解决方法与使用本机 IndexedDB 时相同:进行新事务,或者将事情分开。 + 1. 准备数据,先获取所有需要的信息。 2. 然后保存在数据库中。 diff --git a/figures.sketch b/figures.sketch index 648615914a..1043331923 100644 Binary files a/figures.sketch and b/figures.sketch differ