Skip to content

Update translation of 1-js/06-advanced-functions/03-closure #619

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 23 commits into from
Feb 23, 2020
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
@@ -1,5 +1,5 @@
The answer is: **Pete**.
答案:**Pete**

A function gets outer variables as they are now, it uses the most recent values.
函数将从内到外依次在对应的词法环境中寻找目标变量,它使用最新的值。

Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one.
旧变量值不会保存在任何地方。当一个函数想要一个变量时,它会从自己的词法环境或外部词法环境中获取当前值。
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ importance: 5

---

# Does a function pickup latest changes?
# 函数会选择最新的内容吗?

The function sayHi uses an external variable name. When the function runs, which value is it going to use?
函数 sayHi 使用外部变量。当函数运行时,将使用哪个值?

```js
let name = "John";
Expand All @@ -15,9 +15,9 @@ function sayHi() {

name = "Pete";

sayHi(); // what will it show: "John" or "Pete"?
sayHi(); // 会显示什么:"John" 还是 "Pete"
```

Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request.
这种情况在浏览器和服务器端开发中都很常见。一个函数可能被安排在被创建之后一段时间后才执行,例如在用户操作或网络请求之后。

So, the question is: does it pick up the latest changes?
因此,问题是:它会接收最新的修改吗?
28 changes: 14 additions & 14 deletions 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

如果让我们看一下 `makeArmy` 内做了些什么,那么解答就会变得显而易见
让我们检查一下 `makeArmy` 内部做了什么,那么答案就显而易见了

1. 它创建了一个空数组 `shooters`:

Expand Down Expand Up @@ -29,7 +29,7 @@

随后,`army[5]()` 从数组中获得元素 `army[5]`(函数)并调用。

为什么现在全部的函数都显示一样呢
为什么现在所有函数显示的都一样呢

这是因为 `shooter` 函数内没有局部变量 `i`。当调用一个这样的函数时,`i` 是来自于外部词法环境的。

Expand All @@ -43,21 +43,21 @@ function makeArmy() {
let i = 0;
while (i < 10) {
let shooter = function() { // shooter 函数
alert( i ); // 应该显示它自己的数字
alert( i ); // 应该显示它自己的编号
};
...
}
...
}
```

...我们可以看到它存在于当前 `makeArmy()` 运行相关的词法环境中。但调用 `army[5]()` 时,`makeArmy` 已经完成它的工作,`i` 已经为结束的值:`10`(`while` 结束后)。
……我们可以看到它存在于当前 `makeArmy()` 运行相关的词法环境中。但调用 `army[5]()` 时,`makeArmy` 已经完运行完了,`i` 现在为结束时的值:`10`(`while` 结束时)。

作为结果,所有的 `shooter` 都是从外部词法环境获得同样一个值最后的 `i=10`。
因此,所有的 `shooter` 获得的都是外部词法环境中的同一个值,即最后的 `i=10`。

修改起来是很简单的
我们可以通过将变量定义移动到循环中来修复它

```js run
```js run demo
function makeArmy() {

let shooters = [];
Expand All @@ -66,7 +66,7 @@ function makeArmy() {
for(let i = 0; i < 10; i++) {
*/!*
let shooter = function() { // shooter 函数
alert( i ); // 应该显示它自己的数字
alert( i ); // 应该显示它自己的编号
};
shooters.push(shooter);
}
Expand All @@ -80,15 +80,15 @@ army[0](); // 0
army[5](); // 5
```

现在正常工作了,因为`for (..) {...}` 内的代码块每次执行都会创建一个新的词法环境,其中具有对应的 `i` 的值
现在正常工作了,因为每次执行代码块 `for (let i=0...) {...}` 中的代码时,都会为其创建一个新的词法环境,其中具有对应的 `i`

所以,现在 `i` 值的距离(显示数字的地方更近了。现在它不是在 `makeArmy()` 词法环境中,而是在对应的当前循环迭代的词法环境中。`shooter` 从它创建的位置获得值
所以,现在 `i` 值的位置更近了(译注:指赚到了更内部的词法环境)。现在它不是在 `makeArmy()` 的词法环境中,而是在与当前循环迭代相对应的词法环境中。这就是它为什么现在可以正常工作了

![](lexenv-makearmy.svg)

这里我们把 `while` 改写为了 `for`。

其他技巧也是可以的,让我们了解一下,以便让我们更好地理解这个问题
其他技巧也是可以的,让我们了解一下,以便更好地理解这个问题


```js run
Expand All @@ -101,7 +101,7 @@ function makeArmy() {
let j = i;
*/!*
let shooter = function() { // shooter 函数
alert( *!*j*/!* ); // 应该显示它自己的数字
alert( *!*j*/!* ); // 应该显示当前的编号
};
shooters.push(shooter);
i++;
Expand All @@ -116,6 +116,6 @@ army[0](); // 0
army[5](); // 5
```

`while` 和 `for` 循环差不多,每次运行都会创建了一个新的词法环境。所以在这里它能确保 `shooter` 能获得正确的值
`while` 和 `for` 循环差不多,每次运行都会创建了一个新的词法环境。所以在这里我们能确保 `shooter` 能够获取正确的值

我们复制 `let j = i`。复制 `i` 的值给循环体内的局部(变量)`j`。基本值是按值传递的,所以实际上,我们获得了属于当前循环迭代的完全独立的副本 `i`。
我们复制 `let j = i`。这个操作创建了循环体局部变量 `j`,并将 `i` 的值复制给了它。原始值是按值传递的,所以实际上,我们获得了属于当前循环迭代的完全独立的 `i` 的副本
14 changes: 7 additions & 7 deletions 1-js/06-advanced-functions/03-closure/10-make-army/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ importance: 5

# 函数大军

下列的代码创建一组 `shooters`。
下列的代码创建一个 `shooters` 数组

每个函数输出它的数字。但有些不对...
每个函数都应该输出其编号。但好像出了点问题……

```js run
function makeArmy() {
Expand All @@ -15,7 +15,7 @@ function makeArmy() {
let i = 0;
while (i < 10) {
let shooter = function() { // shooter 函数
alert( i ); // 应该显示它自己的数字
alert( i ); // 应该显示其编号
};
shooters.push(shooter);
i++;
Expand All @@ -26,10 +26,10 @@ function makeArmy() {

let army = makeArmy();

army[0](); // 0 shooter 显示 10
army[5](); // 5 号也输出 10...
// ... 所有的 shooters 都显示 10 而不是它们的 0, 1, 2, 3...
army[0](); // 编号为 0 shooter 值为 10
army[5](); // 编号为 5 的 shooter 值也是 10……
// ... 所有的 shooter 的值都是 10,而不是他们的编号 0, 1, 2, 3...
```

为什么所有的 shooter 函数显示得都一样呢?修改代码让代码正常工作
为什么所有的 shooter 显示同样的值?修改代码以让代码正常工作

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
The answer is: **Pete**.
答案:**Pete**.

The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference:
下方代码中的函数 `work()` 在其被创建的位置通过外部词法环境引用获取 `name`

![](lexenv-nested-work.svg)

So, the result is `"Pete"` here.
所以这里的结果是 `"Pete"`

But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case the result would be `"John"`.
但如果在 `makeWorker()` 中没有 `let name`,那么将继续向外搜索并最终找到全局变量,正如我们可以从上图中看到的那样。在这种情况下,结果将是 `"John"`
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Which variables are available?
# 哪些变量可用呢?

The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else.
下面的 `makeWorker` 函数创建了另一个函数并返回该函数。可以在其他地方调用这个新函数。

Will it have access to the outer variables from its creation place, or the invocation place, or both?
它是否可以从它被创建的位置或调用位置(或两者)访问外部变量?

```js
function makeWorker() {
Expand All @@ -23,7 +23,7 @@ let name = "John";
let work = makeWorker();

// call it
work(); // what will it show?
work(); // 会显示什么?
```

Which value it will show? "Pete" or "John"?
会显示哪个值?"Pete" 还是 "John"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
答案是:**0,1。**

函数 `counter` 和 `counter2` 是通过 `makeCounter` 的不同调用创建的。
函数 `counter` 和 `counter2` 是通过 `makeCounter` 的不同调用创建的。

所以它们都有独立的外部词法环境,每一个都有自己的 `count`。
因此,它们具有独立的外部词法环境,每一个都有自己的 `count`。
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# counters 是独立的吗?
# counter 是独立的吗?

这里我们用相同的 `makeCounter` 函数创建了两个计数器(counters):`counter` 和 `counter`。
在这儿我们用相同的 `makeCounter` 函数创建了两个计数器(counters):`counter` 和 `counter2`。

它们是独立的吗?第二个 counter 要显示什么?`0,1` 或 `2,3` 还是其他?
它们是独立的吗?第二个 counter 会显示什么?`0,1` 或 `2,3` 还是其他?

```js
function makeCounter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

当然行得通。

嵌套函数都是在同一个词法环境中创建的,所以它们可以共享对同一个 count 变量的访问:
这两个嵌套函数都是在同一个词法环境中创建的,所以它们可以共享对同一个 count 变量的访问:

```js run
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ importance: 5

这里通过构造器函数创建了一个 counter 对象。

行得通?它会显示什么呢?
它能正常工作吗?它会显示什么呢?

```js
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
结果是**一个错误**。
答案:**error**。

函数 `sayHi` 是在 `if` 内声明的,所以它只存在于这里面。外部是没有 `sayHi` 的。
函数 `sayHi` 是在 `if` 内声明的,所以它只存在于 `if` 中。外部是没有 `sayHi` 的。
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# if 内的函数

查看这个代码。最后一行执行的结果是什么
看看下面这个代码。最后一行代码的执行结果是什么

```js run
let phrase = "Hello";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ importance: 4

---

# 闭包 sum(函数)
# 闭包 sum

编写一个像 `sum(a)(b) = a+b` 这样工作的 `sum` 函数。

Expand Down
20 changes: 10 additions & 10 deletions 1-js/06-advanced-functions/03-closure/7-let-scope/solution.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The result is: **error**.
答案:**error**

Try running it:
你运行一下试试:

```js run
let x = 1;
Expand All @@ -15,20 +15,20 @@ function func() {
func();
```

In this example we can observe the peculiar difference between a "non-existing" and "unitialized" variable.
在这个例子中,我们可以观察到“不存在”的变量和“未初始化”的变量之间的特殊差异。

As you may have read in the article [](info:closure), a variable starts in the "uninitialized" state from the moment when the execution enters a code block (or a function). And it stays uninitalized until the corresponding `let` statement.
你可能已经在 [](info:closure) 中学过了,从程序执行进入代码块(或函数)的那一刻起,变量就开始进入“未初始化”状态。它一直保持未初始化状态,直至程序执行到相应的 `let` 语句。

In other words, a variable technically exists, but can't be used before `let`.
换句话说,一个变量从技术的角度来讲是存在的,但是在 `let` 之前还不能使用。

The code above demonstrates it.
下面的这段代码证实了这一点。

```js
function func() {
*!*
// the local variable x is known to the engine from the beginning of the function,
// but "unitialized" (unusable) until let ("dead zone")
// hence the error
// 引擎从函数开始就知道局部变量 x,
// 但是变量 x 一直处于“未初始化”(无法使用)的状态,直到结束 let(“死区”)
// 因此答案是 error
*/!*

console.log(x); // ReferenceError: Cannot access 'vx before initialization
Expand All @@ -37,4 +37,4 @@ function func() {
}
```

This zone of temporary unusability of a variable (from the beginning of the code block till `let`) is sometimes called the "dead zone".
变量暂时无法使用的区域(从代码块的开始到 `let`)有时被称为“死区”。
6 changes: 3 additions & 3 deletions 1-js/06-advanced-functions/03-closure/7-let-scope/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ importance: 4

---

# Is variable visible?
# 变量可见吗?

What will be the result of this code?
下面这段代码的结果会是什么?

```js
let x = 1;
Expand All @@ -18,4 +18,4 @@ function func() {
func();
```

P.S. There's a pitfall in this task. The solution is not obvious.
P.S. 这个任务有一个陷阱。解决方案并不明显。
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6

# inArray 筛选器

```js run
```js run demo
function inArray(arr) {
return function(x) {
return arr.includes(x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ importance: 5

# 通过函数筛选

数组中有个内建的 `arr.filter(f)` 方法。它通过函数 `f` 过滤元素。如果元素返回 `true`,那么该元素会被返回到结果数组中。
我们有一个内建的数组方法 `arr.filter(f)`。它通过函数 `f` 过滤元素。如果它返回 `true`,那么该元素会被返回到结果数组中。

制造一系列『马上能用』的过滤器
制造一系列“即用型”过滤器

- `inBetween(a, b)` —— 在 `a` 和 `b` 之间或与它们相等(包括)。
- `inArray([...])` —— 包含在给定的数组中。

用法如下所示:

- `arr.filter(inBetween(3,6))` —— 只挑选 3 6 之间的值
- `arr.filter(inArray([1,2,3]))` —— 只挑选与 `[1,2,3]` 其中成员匹配的元素
- `arr.filter(inBetween(3,6))` —— 只挑选范围在 3 6 的值
- `arr.filter(inArray([1,2,3]))` —— 只挑选与 `[1,2,3]` 中的元素匹配的元素

举个例子
例如

```js
/* .. inBetween 和 inArray 的代码 */
Expand Down
4 changes: 2 additions & 2 deletions 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ users.sort(byField('name'));
users.sort(byField('age'));
```

那么,我们只需要写 `byField(fieldName)`,而不是写一个函数。
这样我们就只需要写 `byField(fieldName)`,而不是写一个函数。

编写可用于此目的的函数 `byField`。
编写函数 `byField` 来实现这个需求
Loading