Skip to content

Update: 1-js/02-first-steps/12-while-for #447

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ while (i) {
```js
let i = 3;

alert(i--); // shows 3, decreases i to 2
alert(i--); // 显示 3,i 减至 2

alert(i--) // shows 2, decreases i to 1
alert(i--) // 显示 2,i 减至 1

alert(i--) // shows 1, decreases i to 0
alert(i--) // 显示 1,i 减至 0

// done, while(i) check stops the loop
// 完成,while(i) 检查并停止循环
```
2 changes: 1 addition & 1 deletion 1-js/02-first-steps/12-while-for/4-for-even/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ importance: 5

使用 `for` 循环输出从 `2` 到 `10` 的偶数。

[演示]
[demo]
10 changes: 5 additions & 5 deletions 1-js/02-first-steps/12-while-for/7-list-primes/solution.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ For each i in the interval {
let n = 10;

nextPrime:
for (let i = 2; i <= n; i++) { // for each i...
for (let i = 2; i <= n; i++) { // 对每个自然数

for (let j = 2; j < i; j++) { // look for a divisor..
if (i % j == 0) continue nextPrime; // not a prime, go next i
for (let j = 2; j < i; j++) { // 寻找一个除数……
if (i % j == 0) continue nextPrime; // 不是素数,则继续检查下一个
}

alert( i ); // a prime
alert( i ); // 输出素数
}
```

这段代码有很大空间可以优化。例如,我们可以从 `2` 到 `i` 的平方根中寻找除数。但无论如何,如果我们想要在很大的时间间隔内实现高效率,我们需要改变方法,依赖高等数学和复杂算法,如[二次筛选] [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) 等等。
这段代码有很大空间可以优化。例如,我们可以从 `2` 到 `i` 的平方根中寻找除数。但无论如何,如果我们想要在很大的时间间隔内实现高效率,我们需要改变方法,依赖高等数学和复杂算法,如[Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) 等等。
81 changes: 40 additions & 41 deletions 1-js/02-first-steps/12-while-for/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ while (i < 3) { // 结果分别是 0、1、2

循环体的单次执行叫作**一次迭代**。上面示例中的循环进行三次迭代。

如果上述示例中没有 `i++`,那么循环(理论上)会永远重复。实际上,浏览器提供了阻止这种循环的方法,对于服务器端 JavaScript,我们可以终止该过程
如果上述示例中没有 `i++`,那么循环(理论上)会永远重复。实际上,浏览器提供了阻止这种循环的方法,对于服务器端 JavaScript,我们可以终止该进程

任何表达式或变量都可以是循环条件,而不仅仅是比较。对它们进行计算,并通过 `while` 将其结果转化为布尔值。

Expand All @@ -47,8 +47,8 @@ while (i) { // 当 i 变成 0 时,条件为 false,循环终止
}
```

````smart header="Brackets are not required for a single-line body"
如果循环体只有一条语句,则可以省略括号 `{…}`:
````smart header="对于单行循环体,大括号并不需要"
如果循环体只有一条语句,则可以省略大括号 `{…}`:

```js run
let i = 3;
Expand Down Expand Up @@ -84,43 +84,45 @@ do {

## "for" 循环

`for` 循环是最常使用的
`for` 循环更加复杂,但它是最常使用的循环形式

看起来就像这样:

```js
for (begin; condition; step) {
// ... loop body ...
// ……循环体……
}
```

我们通过示例来了解这部分的含义。下述循环运行从 `i` 等于 `0` 到 `3`(但不包括 `3`) `alert(i)`:
我们通过示例来了解这三个语句段(part)的含义。下述循环从 `i` 等于 `0` 到 `3`(但不包括 `3`)运行 `alert(i)`:

```js run
for (let i = 0; i < 3; i++) { // 结果为 0、1、2
alert(i);
}
```

我们逐部分地检查 `for` 语句:
我们逐个语句段分析 `for` 语句:

| 部分 | | |
| 语句段 | | |
| -----|----------|----------------------------------------------------------------------------|
| 开始 | `i = 0` | 进入循环时执行一次。 |
| 条件 | `i < 3`| 在每次循环迭代之前检查,如果失败,循环停止。 |
| 步骤 | `i++` | 在每次迭代后执行主体,但在条件检查之前执行。 |
| 主体 | `alert(i)`| 条件为真时,重复运行。 |

| begin | `i = 0` | 进入循环时执行一次。 |
| condition | `i < 3`| 在每次循环迭代之前检查,如果失败,循环停止。 |
| step | `i++` | 主体每次迭代后执行,但在条件检查之前执行。 |
| body(循环体) | `alert(i)`| 条件为真时,重复运行。 |

一般循环算法的工作原理如下:

```
Run begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
开始运行
→ (如果 condition 成立 → 运行 body 然后运行 step)
→ (如果 condition 成立 → 运行 body 然后运行 step)
→ (如果 condition 成立 → 运行 body 然后运行 step)
→ ...
```

所以,`begin` 执行一次,然后进行迭代:每次检查 `condition` 后,`body` 和 `step` 被执行。

如果您是循环方面的小白,那么回到这个例子,在一张纸上重现它逐步运行的过程,可能会对你有所帮助。

以下是我们示例中发生的情况:
Expand All @@ -139,7 +141,7 @@ if (i < 3) { alert(i); i++ }
// ...结束,因为现在 i == 3
```

````smart header="Inline variable declaration"
````smart header="内联变量声明"
这里“计数”变量 `i` 在循环中声明。这叫做“内联”变量声明。这样的变量只在循环中可见。

```js run
Expand All @@ -164,23 +166,23 @@ alert(i); //3,可见,因为在循环之外声明
````


### 跳过
### 省略语句段

`for` 循环的任何部分都可以被跳过
`for` 循环的任何语句段都可以被省略

例如,如果我们在循环开始时不需要做任何事,我们可以省略 `begin` 部分
例如,如果我们在循环开始时不需要做任何事,我们可以省略 `begin` 语句段

就像这样:

```js run
let i = 0; // 我们已经声明并分配了
let i = 0; // 我们已经声明了 i 并对它进行赋值了

for (; i < 3; i++) { // "begin" 部分不再需要
for (; i < 3; i++) { // "begin" 语句段不再需要
alert( i ); // 0, 1, 2
}
```

我们也可以移除 `step` 部分
我们也可以移除 `step` 语句段

```js run
let i = 0;
Expand Down Expand Up @@ -245,13 +247,13 @@ for (let i = 0; i < 10; i++) {
 //如果为真,跳过循环体的剩余部分。
*!*if (i % 2 == 0) continue;*/!*

alert(i); // 1、3、5、7、9
alert(i); // 1,然后 3,5,7,9
}
```

对于偶数的 `i`,`continue` 指令停止执行,将控制权传递给下一次 `for`(使用下一个数字)的迭代。因此 `alert` 仅被奇数值调用。
对于偶数的 `i`,`continue` 指令停止了循环体的继续执行,将控制权传递给下一次 `for`(使用下一个数字)的迭代。因此 `alert` 仅被奇数值调用。

````smart header="The directive `continue` helps to decrease nesting level"
````smart header="`continue` 指令利于减少嵌套"
显示奇数的循环如下所示:

```js
Expand All @@ -266,10 +268,10 @@ for (let i = 0; i < 10; i++) {

从技术角度看,它与上述示例完全相同。当然,我们可以将代码包装在 `if` 块而不是 `continue` 块。

但作为副作用,我们还有一个嵌套级别(花括号内的 `alert` 调用)。如果 `if` 中代码超过几行,则可能会降低总体可读性。
但至于副作用,它创建多一个嵌套级别(大括号内的 `alert` 调用)。如果 `if` 中代码超过几行,则可能会降低总体可读性。
````

````warn header="No `break/continue` to the right side of '?'"
````warn header="禁止 `break/continue` 在‘?’的右边"
请注意非表达式的语法结构不能与三元运算符 `?` 一起使用。特别是 `break/continue` 这样的指令是不被允许使用的。

例如,我们使用如下代码:
Expand All @@ -282,17 +284,16 @@ if (i > 5) {
}
```

...然后用问号重写:
……然后用问号重写:


```js no-beautify
(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here
(i > 5) ? alert(i) : *!*continue*/!*; // continue 不允许在这个位置
```

...然后会停止运行。这样的代码将给出语法错误:
……然后会停止运行。这样的代码将给出语法错误:


这只是不适用 `?` 而不是 `if` 的另一个原因。
这只是不使用 `?` 而不是 `if` 的另一个原因。
````

## break/continue 标签
Expand All @@ -318,7 +319,7 @@ alert('Done!');

如果用户取消输入,我们需要另一种方法来停止这个过程。

在 `input` 之后的普通 `break` 只会打破内部循环。这还不够标签可以拯救。
在 `input` 之后的普通 `break` 只会打破内部循环。这还不够 —— 标签可以拯救。

**标签**是在循环之前带有冒号的标识符:
```js
Expand All @@ -327,9 +328,7 @@ labelName: for (...) {
}
```

`break <labelName>` 语句跳出循环至标签处。

就像这样:
`break <labelName>` 语句跳出循环至标签处:

```js run no-beautify
*!*outer:*/!* for (let i = 0; i < 3; i++) {
Expand All @@ -341,7 +340,7 @@ labelName: for (...) {
// 如果是空字符串或已取消,则中断这两个循环。
if (!input) *!*break outer*/!*; // (*)

// 做些有价值的事
// 用得到的值做些事……
}
}
alert('Done!');
Expand All @@ -360,7 +359,7 @@ for (let i = 0; i < 3; i++) { ... }

`continue` 指令也可以与标签一起使用。在这种情况下,执行跳转到标记循环的下一次迭代。

````warn header="Labels are not a \"goto\""
````warn header="标签不允许“跳到”任何位置"
标签不允许我们跳到任意代码位置。

例如,这样做是不可能的:
Expand All @@ -385,4 +384,4 @@ label: for (...)

如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么 `continue` 指令就会执行它。

`break/continue` 支持循环前的标签。标签是 `break/continue` 避免嵌套并转到外部循环的唯一方法
`break/continue` 支持循环前的标签。标签是 `break/continue` 跳出嵌套循环来转到外部循环的唯一方法