Skip to content
2 changes: 1 addition & 1 deletion 1-js/02-first-steps/16-arrow-functions-basics/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ let welcome = (age < 18) ?
() => alert('Hello') :
() => alert("Greetings!");

welcome(); // 现在好了
welcome();
```

一开始,箭头函数可能看起来并不熟悉,也不容易读懂,但一旦我们看习惯了之后,这种情况很快就会改变。
Expand Down
32 changes: 16 additions & 16 deletions 1-js/03-code-quality/03-comments/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,25 +125,25 @@ function addJuice(container) {
记录函数的参数和用法
: 有一个专门用于记录函数的语法 [JSDoc](http://en.wikipedia.org/wiki/JSDoc):用法、参数和返回值。

例如:
```js
/**
* 返回 x 的 n 次幂的值。
*
* @param {number} x 要改变的值。
* @param {number} n 幂数,必须是一个自然数。
* @return {number} x 的 n 次幂的值。
*/
function pow(x, n) {
...
}
```
例如:
```js
/**
* 返回 x 的 n 次幂的值。
*
* @param {number} x 要改变的值。
* @param {number} n 幂数,必须是一个自然数。
* @return {number} x 的 n 次幂的值。
*/
function pow(x, n) {
...
}
```

这种注释可以帮助我们理解函数的目的,并且不需要研究其内部的实现代码,就可以直接正确地使用它。
这种注释可以帮助我们理解函数的目的,并且不需要研究其内部的实现代码,就可以直接正确地使用它。

顺便说一句,很多诸如 [WebStorm](https://www.jetbrains.com/webstorm/) 这样的编辑器,都可以很好地理解和使用这些注释,来提供自动补全和一些自动化代码检查工作。
顺便说一句,很多诸如 [WebStorm](https://www.jetbrains.com/webstorm/) 这样的编辑器,都可以很好地理解和使用这些注释,来提供自动补全和一些自动化代码检查工作。

当然,也有一些像 [JSDoc 3](https://github.com/jsdoc3/jsdoc) 这样的工具,可以通过注释直接生成 HTML 文档。你可以在 <http://usejsdoc.org/> 阅读更多关于 JSDoc 的信息。
当然,也有一些像 [JSDoc 3](https://github.com/jsdoc3/jsdoc) 这样的工具,可以通过注释直接生成 HTML 文档。你可以在 <http://usejsdoc.org/> 阅读更多关于 JSDoc 的信息。

为什么任务以这种方式解决?
: 写了什么代码很重要。但是为什么 **不** 那样写可能对于理解正在发生什么更重要。为什么任务是通过这种方式解决的?代码并没有给出答案。
Expand Down
4 changes: 2 additions & 2 deletions 1-js/04-object-basics/01-object/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ let bag = {
function makeUser(name, age) {
return {
name: name,
age: age
age: age,
// ……其他的属性
};
}
Expand All @@ -233,7 +233,7 @@ function makeUser(name, age) {
*!*
return {
name, // 与 name: name 相同
age // 与 age: age 相同
age, // 与 age: age 相同
// ...
};
*/!*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago"

alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"

// 昨天的日期如:31.12.2016, 20:00
// 昨天的日期如:31.12.2016 20:00
alert( formatDate(new Date(new Date - 86400 * 1000)) );
```

Expand Down
2 changes: 1 addition & 1 deletion 1-js/05-data-types/11-date/8-format-date-relative/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago"

alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"

// yesterday's date like 31.12.16, 20:00
// 昨天的日期,例如 31.12.16 20:00
alert( formatDate(new Date(new Date - 86400 * 1000)) );
```
2 changes: 1 addition & 1 deletion 1-js/08-prototypes/02-function-prototype/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ alert( rabbit.eats ); // true
在上图中,`"prototype"` 是一个水平箭头,表示一个常规属性,`[[Prototype]]` 是垂直的,表示 `rabbit` 继承自 `animal`。

```smart header="`F.prototype` 仅用在 `new F` 时"
`F.prototype` 属性仅在 `new F` 被调用时使用,它为新对象的 `[[Prototype]]` 赋值。之后,`F.prototype` 和新对象之间就没有任何联系了。可以把它看成“一次性的礼物”。
`F.prototype` 属性仅在 `new F` 被调用时使用,它为新对象的 `[[Prototype]]` 赋值。

如果在创建之后,`F.prototype` 属性有了变化(`F.prototype = <another object>`),那么通过 `new F` 创建的新对象也将随之拥有新的对象作为 `[[Prototype]]`,但已经存在的对象将保持旧有的值。
```
Expand Down
36 changes: 32 additions & 4 deletions 1-js/10-error-handling/2-custom-errors/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,39 @@ alert( new PropertyRequiredError("field").name ); // PropertyRequiredError

在上面代码中的函数 `readUser` 的目的就是“读取用户数据”。在这个过程中可能会出现不同类型的 error。目前我们有了 `SyntaxError` 和 `ValidationError`,但是将来,函数 `readUser` 可能会不断壮大,并可能会产生其他类型的 error。

调用 `readUser` 的代码应该处理这些 error。现在它在 `catch` 块中使用了多个 `if` 语句来检查 error 类,处理已知的 error,并再次抛出未知的 error。但是如果函数 `readUser` 产生了多种 error,那么我们应该扪心自问:我们真的想在每个调用 `readUser` 的代码中都一一检查所有的 error 类型吗?
调用 `readUser` 的代码应该处理这些 error。现在它在 `catch` 块中使用了多个 `if` 语句来检查 error 类,处理已知的 error,并再次抛出未知的 error。

通常答案是 "No":外部代码希望“比它高一个级别”,外部代码只想具有几种“数据读取异常” — 为什么发生了这样的 error 通常是无关紧要的(error 信息描述了它)。或者,如果能有一种方法能够获取 error 的详细信息那就更好了,但前提是我们需要这样做。
该方案是这样的:

因此,让我们创建一个新的类 `ReadError` 来表示此类 error。如果在 `readUser` 内部发生了 error,我们将在那里捕获这个 error 并生成 `ReadError`。我们也会在其 `cause` 属性中保留对原始 error 的引用。然后,外部代码将只需要检查 `ReadError`。
```js
try {
...
readUser() // 潜在的 error 源
...
} catch (err) {
if (err instanceof ValidationError) {
// 处理 validation error
} else if (err instanceof SyntaxError) {
// 处理 syntax error
} else {
throw err; // 未知 error,再次抛出它
}
}
```

在上面的代码中,我们可以看到两种类型的 error,但是可以有更多。

如果 `readUser` 函数会产生多种 error,那么我们应该问问自己:我们是否真的想每次都一一检查所有的 error 类型?

通常答案是 "No":我们希望能够“比它高一个级别”。我们只想知道这里是否是“数据读取异常” — 为什么发生了这样的 error 通常是无关紧要的(error 信息描述了它)。或者,如果我们有一种方法能够获取 error 的详细信息那就更好了,但前提是我们需要。

我们所描述的这项技术被称为“包装异常”。

1. 我们将创建一个新的类 `ReadError` 来表示一般的“数据读取” error。
2. 函数`readUser` 将捕获内部发生的数据读取 error,例如 `ValidationError` 和 `SyntaxError`,并生成一个 `ReadError` 来进行替代。
3. 对象 `ReadError` 会把对原始 error 的引用保存在其 `cause` 属性中。

之后,调用 `readUser` 的代码只需要检查 `ReadError`,而不必检查每种数据读取 error。并且,如果需要更多 error 细节,那么可以检查 `readUser` 的 `cause` 属性。

下面的代码定义了 `ReadError`,并在 `readUser` 和 `try..catch` 中演示了其用法:

Expand Down Expand Up @@ -293,7 +321,7 @@ try {

所以外部代码检查 `instanceof ReadError`,并且它的确是。不必列出所有可能的 error 类型。

这种方法被称为“包装异常(wrapping exceptions)”,因为我们将“低级别的异常”包装到 `ReadError` 中,这对于调用代码来说更加抽象和方便。它被广泛应用于面向对象的编程中。
这种方法被称为“包装异常(wrapping exceptions)”,因为我们将“低级别”的异常“包装”到了更抽象的 `ReadError` 中。它被广泛应用于面向对象的编程中。

## 总结

Expand Down
10 changes: 5 additions & 5 deletions 1-js/11-async/01-callbacks/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
但是,我们会尽全力使讲解变得更加清晰。在这儿不会有浏览器方面的真正复杂的东西。
```

JavaScipt 中的许多行为(action)都是 **异步的**。换句话说,我们现在启动它们,但在稍后再完成
JavaScipt 主机(host)环境提供了许多函数,这些函数允许我们安排 **异步** 动作(action)。换句话说,我们现在开始执行的动作,但它们会在稍后完成

例如,我们可以使用 `setTimeout` 来安排此类行为
例如,`setTimeout` 函数就是一个这样的函数

这儿有一些实际中的异步行为的示例,例如加载脚本和模块(我们将在后面的章节中介绍)。
这儿有一些实际中的异步动作的示例,例如加载脚本和模块(我们将在后面的章节中介绍)。

让我们看一下函数 `loadScript(src)`,该函数使用给定的 `src` 加载脚本:

Expand Down Expand Up @@ -263,7 +263,7 @@ loadScript('1.js', function(error, script) {

![](callback-hell.svg)

嵌套调用的“金字塔”随着每个异步行为会向右增长。很快它就失控了。
嵌套调用的“金字塔”随着每个异步动作会向右增长。很快它就失控了。

所以这种编码方式不是很好。

Expand Down Expand Up @@ -307,4 +307,4 @@ function step3(error, script) {

我们希望还有更好的方法。

幸运地是,有其他方法可以避免此类金字塔。最好的方法之一就是 "promise",我们将在下一章中介绍它。
幸运的是,有其他方法可以避免此类金字塔。最好的方法之一就是 "promise",我们将在下一章中介绍它。
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
background-color: #00FF00;
position: relative;
}

#ball {
position: absolute;
}
Expand All @@ -20,7 +20,7 @@


<div id="field">
<img src="https://js.cx/clipart/ball.svg" id="ball"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
<img src="https://js.cx/clipart/ball.svg" height="40" width="40" id="ball"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
</div>

Expand All @@ -38,4 +38,4 @@

</body>

</html>
</html>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
小球具有 `position:absolute`。这意味着它的 `left/top` 坐标是从最近的具有定位属性的元素开始测量的,这个元素即 `#field`(因为它有 `position:relative`)。
球具有 `position:absolute`。这意味着它的 `left/top` 坐标是从最近的具有定位属性的元素开始测量的,这个元素即 `#field`(因为它有 `position:relative`)。

坐标从场(field)的左上角内侧开始:

Expand All @@ -24,17 +24,22 @@ ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px
ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px';
```

**注意:陷阱!**
现在,球终于居中了。

````warn header="注意:陷阱!"

当 `<img>` 没有 width/height 时,代码将无法可靠地工作:

```html
<img src="ball.png" id="ball">
```
````

当浏览器不知道图片的 width/height(通过标签 attribute 或 CSS)时,它会假定它们等于 `0`,直到图片加载完成。

但在实际中,第一次加载后,浏览器通常会缓存该图片,并在下一次加载时,浏览器会立即拥有该图片的大小。但是在第一次加载时, `ball.offsetWidth` 的值为 `0`,这会导致错误的坐标。
因此,在图片加载完成之前,`ball.offsetWidth` 的值为 `0`。这会导致上面的代码中会有错误的坐标。

在第一次加载完成后,浏览器通常会缓存该图片,并在下一次加载时,浏览器会立即拥有该图片的大小。但是在第一次加载时,`ball.offsetWidth` 的值为 `0`。

我们应该通过在 `<img>` 中添加 `width/height` 来解决这个问题:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@


<script>
// ball.offsetWidth=0 before image loaded!
// ball.offsetWidth=0 before image loaded!
// to fix: set width
ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px'
ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ importance: 5

场(field)的中心坐标是多少?

计算它们,并将小球置于场(field)中心:
计算它们,并将小球置于绿色的场(field)中心:

[iframe src="solution" height=180]

Expand Down